diff --git a/.classpath b/.classpath
index 77e8068a..dc543d95 100644
--- a/.classpath
+++ b/.classpath
@@ -17,5 +17,6 @@
+
diff --git a/lib/hsqldb-r5836.jar b/lib/hsqldb-r5836.jar
new file mode 100644
index 00000000..72721edf
Binary files /dev/null and b/lib/hsqldb-r5836.jar differ
diff --git a/pom.xml b/pom.xml
index 078e0af6..bdf7f1b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,11 +17,6 @@
-
- org.hsqldb
- hsqldb
- 2.4.0
-
com.googlecode.json-simple
json-simple
diff --git a/src/data/transaction/CancelOrderTransactionData.java b/src/data/transaction/CancelOrderTransactionData.java
index b4dff9bc..126116e7 100644
--- a/src/data/transaction/CancelOrderTransactionData.java
+++ b/src/data/transaction/CancelOrderTransactionData.java
@@ -7,7 +7,6 @@ import qora.transaction.Transaction;
public class CancelOrderTransactionData extends TransactionData {
// Properties
- private byte[] creatorPublicKey;
private byte[] orderId;
// Constructors
@@ -15,7 +14,6 @@ public class CancelOrderTransactionData extends TransactionData {
public CancelOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(Transaction.TransactionType.CANCEL_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
- this.creatorPublicKey = creatorPublicKey;
this.orderId = orderId;
}
@@ -25,10 +23,6 @@ public class CancelOrderTransactionData extends TransactionData {
// Getters/Setters
- public byte[] getCreatorPublicKey() {
- return this.creatorPublicKey;
- }
-
public byte[] getOrderId() {
return this.orderId;
}
diff --git a/src/data/transaction/CreateOrderTransactionData.java b/src/data/transaction/CreateOrderTransactionData.java
index c157c8ba..95bd2cfe 100644
--- a/src/data/transaction/CreateOrderTransactionData.java
+++ b/src/data/transaction/CreateOrderTransactionData.java
@@ -7,7 +7,6 @@ import qora.transaction.Transaction.TransactionType;
public class CreateOrderTransactionData extends TransactionData {
// Properties
- private byte[] creatorPublicKey;
private long haveAssetId;
private long wantAssetId;
private BigDecimal amount;
@@ -19,7 +18,6 @@ public class CreateOrderTransactionData extends TransactionData {
long timestamp, byte[] reference, byte[] signature) {
super(TransactionType.CREATE_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
- this.creatorPublicKey = creatorPublicKey;
this.haveAssetId = haveAssetId;
this.wantAssetId = wantAssetId;
this.amount = amount;
@@ -33,10 +31,6 @@ public class CreateOrderTransactionData extends TransactionData {
// Getters/Setters
- public byte[] getCreatorPublicKey() {
- return this.creatorPublicKey;
- }
-
public long getHaveAssetId() {
return this.haveAssetId;
}
diff --git a/src/data/transaction/CreatePollTransactionData.java b/src/data/transaction/CreatePollTransactionData.java
index 4d6cd111..78fd0862 100644
--- a/src/data/transaction/CreatePollTransactionData.java
+++ b/src/data/transaction/CreatePollTransactionData.java
@@ -9,7 +9,6 @@ import qora.transaction.Transaction;
public class CreatePollTransactionData extends TransactionData {
// Properties
- private byte[] creatorPublicKey;
private String owner;
private String pollName;
private String description;
@@ -21,7 +20,6 @@ public class CreatePollTransactionData extends TransactionData {
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
super(Transaction.TransactionType.CREATE_POLL, fee, creatorPublicKey, timestamp, reference, signature);
- this.creatorPublicKey = creatorPublicKey;
this.owner = owner;
this.pollName = pollName;
this.description = description;
@@ -35,10 +33,6 @@ public class CreatePollTransactionData extends TransactionData {
// Getters/setters
- public byte[] getCreatorPublicKey() {
- return this.creatorPublicKey;
- }
-
public String getOwner() {
return this.owner;
}
diff --git a/src/migrate.java b/src/migrate.java
index 2583153e..76aa541f 100644
--- a/src/migrate.java
+++ b/src/migrate.java
@@ -44,21 +44,14 @@ public class migrate {
private static Map publicKeyByAddress = new HashMap();
public static Object fetchBlockJSON(int height) throws IOException {
- InputStream is;
-
- try {
- is = new URL("http://localhost:9085/blocks/byheight/" + height).openStream();
+ try (InputStream is = new URL("http://localhost:9085/blocks/byheight/" + height).openStream();
+ InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"));
+ BufferedReader reader = new BufferedReader(isr)) {
+ return JSONValue.parseWithException(reader);
} catch (IOException e) {
return null;
- }
-
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
- return JSONValue.parseWithException(reader);
} catch (ParseException e) {
return null;
- } finally {
- is.close();
}
}
@@ -69,8 +62,8 @@ public class migrate {
InputStream is = new URL("http://localhost:9085/addresses/publickey/" + address).openStream();
- try {
- String publicKey58 = CharStreams.toString(new InputStreamReader(is, Charset.forName("UTF-8")));
+ try (InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"))) {
+ String publicKey58 = CharStreams.toString(isr);
byte[] publicKey = Base58.decode(publicKey58);
publicKeyByAddress.put(address, publicKey);
@@ -81,12 +74,12 @@ public class migrate {
}
public static void savePublicKeys(Connection connection) throws SQLException {
- PreparedStatement pStmt = connection.prepareStatement("INSERT IGNORE INTO Test_public_keys VALUES (?, ?)");
-
- for (Entry entry : publicKeyByAddress.entrySet()) {
- pStmt.setString(1, entry.getKey());
- pStmt.setBytes(2, entry.getValue());
- pStmt.execute();
+ try (PreparedStatement pStmt = connection.prepareStatement("INSERT IGNORE INTO Test_public_keys VALUES (?, ?)")) {
+ for (Entry entry : publicKeyByAddress.entrySet()) {
+ pStmt.setString(1, entry.getKey());
+ pStmt.setBytes(2, entry.getValue());
+ pStmt.execute();
+ }
}
}
@@ -103,6 +96,7 @@ public class migrate {
return output.toString();
}
+ @SuppressWarnings("resource")
public static void main(String args[]) throws SQLException, DataException, IOException {
// Genesis public key
publicKeyByAddress.put(GENESIS_ADDRESS, GENESIS_PUBLICKEY);
@@ -132,14 +126,14 @@ public class migrate {
.prepareStatement("INSERT INTO PaymentTransactions " + formatWithPlaceholders("signature", "sender", "recipient", "amount"));
PreparedStatement registerNamePStmt = c
.prepareStatement("INSERT INTO RegisterNameTransactions " + formatWithPlaceholders("signature", "registrant", "name", "owner", "data"));
- PreparedStatement updateNamePStmt = c
- .prepareStatement("INSERT INTO UpdateNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "new_owner", "new_data", "name_reference"));
+ PreparedStatement updateNamePStmt = c.prepareStatement(
+ "INSERT INTO UpdateNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "new_owner", "new_data", "name_reference"));
PreparedStatement sellNamePStmt = c
.prepareStatement("INSERT INTO SellNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "amount"));
PreparedStatement cancelSellNamePStmt = c
.prepareStatement("INSERT INTO CancelSellNameTransactions " + formatWithPlaceholders("signature", "owner", "name"));
- PreparedStatement buyNamePStmt = c
- .prepareStatement("INSERT INTO BuyNameTransactions " + formatWithPlaceholders("signature", "buyer", "name", "seller", "amount", "name_reference"));
+ PreparedStatement buyNamePStmt = c.prepareStatement(
+ "INSERT INTO BuyNameTransactions " + formatWithPlaceholders("signature", "buyer", "name", "seller", "amount", "name_reference"));
PreparedStatement createPollPStmt = c
.prepareStatement("INSERT INTO CreatePollTransactions " + formatWithPlaceholders("signature", "creator", "owner", "poll_name", "description"));
PreparedStatement createPollOptionPStmt = c
diff --git a/src/qora/assets/Order.java b/src/qora/assets/Order.java
index 23c98004..aa173628 100644
--- a/src/qora/assets/Order.java
+++ b/src/qora/assets/Order.java
@@ -163,7 +163,7 @@ public class Order {
// Trade can go ahead!
- // Calculate the total cost to us based on their price
+ // Calculate the total cost to us, in have-asset, based on their price
BigDecimal tradePrice = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8);
// Construct trade
@@ -174,7 +174,7 @@ public class Order {
trade.process();
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
- this.orderData.setFulfilled(this.orderData.getFulfilled().add(matchedAmount));
+ this.orderData.setFulfilled(this.orderData.getFulfilled().add(tradePrice));
// Continue on to process other open orders in case we still have amount left to match
}
diff --git a/src/qora/block/BlockChain.java b/src/qora/block/BlockChain.java
index f94d8043..3a2b4074 100644
--- a/src/qora/block/BlockChain.java
+++ b/src/qora/block/BlockChain.java
@@ -38,9 +38,11 @@ public class BlockChain {
private static final long ASSETS_RELEASE_TIMESTAMP = 0L; // From Qora epoch
private static final long VOTING_RELEASE_TIMESTAMP = 1403715600000L; // 2014-06-25T17:00:00+00:00
private static final long ARBITRARY_RELEASE_TIMESTAMP = 1405702800000L; // 2014-07-18T17:00:00+00:00
+
private static final long CREATE_POLL_V2_TIMESTAMP = 1552500000000L; // 2019-03-13T18:00:00+00:00 // Future Qora v2 CREATE POLL transactions
private static final long ISSUE_ASSET_V2_TIMESTAMP = 1552500000000L; // 2019-03-13T18:00:00+00:00 // Future Qora v2 ISSUE ASSET transactions
private static final long CREATE_ORDER_V2_TIMESTAMP = 1552500000000L; // 2019-03-13T18:00:00+00:00 // Future Qora v2 CREATE ORDER transactions
+ private static final long ARBITRARY_TRANSACTION_V2_TIMESTAMP = 1552500000000L; // 2019-03-13T18:00:00+00:00 // Future Qora v2 ARBITRARY transactions
/**
* Some sort start-up/initialization/checking method.
@@ -164,4 +166,11 @@ public class BlockChain {
return CREATE_ORDER_V2_TIMESTAMP;
}
+ public static long getArbitraryTransactionV2Timestamp() {
+ if (Settings.getInstance().isTestNet())
+ return 0;
+
+ return ARBITRARY_TRANSACTION_V2_TIMESTAMP;
+ }
+
}
diff --git a/src/qora/transaction/ArbitraryTransaction.java b/src/qora/transaction/ArbitraryTransaction.java
index 6ca023f3..8fa87ebd 100644
--- a/src/qora/transaction/ArbitraryTransaction.java
+++ b/src/qora/transaction/ArbitraryTransaction.java
@@ -44,6 +44,7 @@ public class ArbitraryTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
List recipients = new ArrayList();
@@ -54,6 +55,7 @@ public class ArbitraryTransaction extends Transaction {
return recipients;
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -68,6 +70,7 @@ public class ArbitraryTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/qora/transaction/CancelOrderTransaction.java b/src/qora/transaction/CancelOrderTransaction.java
index 12012756..3b7708a5 100644
--- a/src/qora/transaction/CancelOrderTransaction.java
+++ b/src/qora/transaction/CancelOrderTransaction.java
@@ -32,14 +32,17 @@ public class CancelOrderTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() {
return new ArrayList();
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
return account.getAddress().equals(this.getCreator().getAddress());
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
BigDecimal amount = BigDecimal.ZERO.setScale(8);
@@ -51,6 +54,7 @@ public class CancelOrderTransaction extends Transaction {
// Navigation
+ @Override
public Account getCreator() throws DataException {
return new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey());
}
diff --git a/src/qora/transaction/CreateOrderTransaction.java b/src/qora/transaction/CreateOrderTransaction.java
index 38a1a63c..4a02d9d8 100644
--- a/src/qora/transaction/CreateOrderTransaction.java
+++ b/src/qora/transaction/CreateOrderTransaction.java
@@ -33,14 +33,17 @@ public class CreateOrderTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() {
return new ArrayList();
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
return account.getAddress().equals(this.getCreator().getAddress());
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
BigDecimal amount = BigDecimal.ZERO.setScale(8);
@@ -52,6 +55,7 @@ public class CreateOrderTransaction extends Transaction {
// Navigation
+ @Override
public Account getCreator() throws DataException {
return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
}
diff --git a/src/qora/transaction/CreatePollTransaction.java b/src/qora/transaction/CreatePollTransaction.java
index 0d009fd1..6658c3ba 100644
--- a/src/qora/transaction/CreatePollTransaction.java
+++ b/src/qora/transaction/CreatePollTransaction.java
@@ -35,10 +35,12 @@ public class CreatePollTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(getOwner());
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -51,6 +53,7 @@ public class CreatePollTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
@@ -63,6 +66,7 @@ public class CreatePollTransaction extends Transaction {
// Navigation
+ @Override
public Account getCreator() throws DataException {
return new PublicKeyAccount(this.repository, this.createPollTransactionData.getCreatorPublicKey());
}
diff --git a/src/qora/transaction/GenesisTransaction.java b/src/qora/transaction/GenesisTransaction.java
index 1e1f55fa..ed2330b8 100644
--- a/src/qora/transaction/GenesisTransaction.java
+++ b/src/qora/transaction/GenesisTransaction.java
@@ -36,10 +36,12 @@ public class GenesisTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(new Account(this.repository, genesisTransactionData.getRecipient()));
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -52,6 +54,7 @@ public class GenesisTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/qora/transaction/IssueAssetTransaction.java b/src/qora/transaction/IssueAssetTransaction.java
index 95d296aa..d3954df6 100644
--- a/src/qora/transaction/IssueAssetTransaction.java
+++ b/src/qora/transaction/IssueAssetTransaction.java
@@ -36,10 +36,12 @@ public class IssueAssetTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(getOwner());
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -52,6 +54,7 @@ public class IssueAssetTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
@@ -117,8 +120,9 @@ public class IssueAssetTransaction extends Transaction {
return ValidationResult.NO_BALANCE;
// XXX: Surely we want to check the asset name isn't already taken? This check is not present in gen1.
- if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName()))
- return ValidationResult.ASSET_ALREADY_EXISTS;
+ if (issueAssetTransactionData.getTimestamp() >= BlockChain.getIssueAssetV2Timestamp())
+ if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName()))
+ return ValidationResult.ASSET_ALREADY_EXISTS;
return ValidationResult.OK;
}
diff --git a/src/qora/transaction/MessageTransaction.java b/src/qora/transaction/MessageTransaction.java
index 3ea62f65..a23b933c 100644
--- a/src/qora/transaction/MessageTransaction.java
+++ b/src/qora/transaction/MessageTransaction.java
@@ -34,10 +34,12 @@ public class MessageTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient()));
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -50,6 +52,7 @@ public class MessageTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/qora/transaction/MultiPaymentTransaction.java b/src/qora/transaction/MultiPaymentTransaction.java
index 8ca52343..321f4822 100644
--- a/src/qora/transaction/MultiPaymentTransaction.java
+++ b/src/qora/transaction/MultiPaymentTransaction.java
@@ -34,6 +34,7 @@ public class MultiPaymentTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
List recipients = new ArrayList();
@@ -43,6 +44,7 @@ public class MultiPaymentTransaction extends Transaction {
return recipients;
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -56,6 +58,7 @@ public class MultiPaymentTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/qora/transaction/PaymentTransaction.java b/src/qora/transaction/PaymentTransaction.java
index 015985d8..6e1ec8d6 100644
--- a/src/qora/transaction/PaymentTransaction.java
+++ b/src/qora/transaction/PaymentTransaction.java
@@ -30,10 +30,12 @@ public class PaymentTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(new Account(this.repository, paymentTransactionData.getRecipient()));
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -46,6 +48,7 @@ public class PaymentTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/qora/transaction/TransferAssetTransaction.java b/src/qora/transaction/TransferAssetTransaction.java
index 3409ffbc..17a8ed7b 100644
--- a/src/qora/transaction/TransferAssetTransaction.java
+++ b/src/qora/transaction/TransferAssetTransaction.java
@@ -31,10 +31,12 @@ public class TransferAssetTransaction extends Transaction {
// More information
+ @Override
public List getRecipientAccounts() throws DataException {
return Collections.singletonList(new Account(this.repository, transferAssetTransactionData.getRecipient()));
}
+ @Override
public boolean isInvolved(Account account) throws DataException {
String address = account.getAddress();
@@ -47,6 +49,7 @@ public class TransferAssetTransaction extends Transaction {
return false;
}
+ @Override
public BigDecimal getAmount(Account account) throws DataException {
String address = account.getAddress();
BigDecimal amount = BigDecimal.ZERO.setScale(8);
diff --git a/src/repository/Repository.java b/src/repository/Repository.java
index b9af67cd..fa969947 100644
--- a/src/repository/Repository.java
+++ b/src/repository/Repository.java
@@ -18,6 +18,7 @@ public interface Repository extends AutoCloseable {
public void discardChanges() throws DataException;
+ @Override
public void close() throws DataException;
public void rebuild() throws DataException;
diff --git a/src/repository/hsqldb/HSQLDBAccountRepository.java b/src/repository/hsqldb/HSQLDBAccountRepository.java
index b75ca7f1..1e2c669e 100644
--- a/src/repository/hsqldb/HSQLDBAccountRepository.java
+++ b/src/repository/hsqldb/HSQLDBAccountRepository.java
@@ -17,9 +17,9 @@ public class HSQLDBAccountRepository implements AccountRepository {
this.repository = repository;
}
+ @Override
public AccountData getAccount(String address) throws DataException {
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address)) {
if (resultSet == null)
return null;
@@ -29,6 +29,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
}
}
+ @Override
public void save(AccountData accountData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
saveHelper.bind("account", accountData.getAddress()).bind("reference", accountData.getReference());
@@ -40,9 +41,9 @@ public class HSQLDBAccountRepository implements AccountRepository {
}
}
+ @Override
public AccountBalanceData getBalance(String address, long assetId) throws DataException {
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId)) {
if (resultSet == null)
return null;
@@ -54,6 +55,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
}
}
+ @Override
public void save(AccountBalanceData accountBalanceData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance",
@@ -66,6 +68,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
}
}
+ @Override
public void delete(String address, long assetId) throws DataException {
try {
this.repository.delete("AccountBalances", "account = ? and asset_id = ?", address, assetId);
diff --git a/src/repository/hsqldb/HSQLDBAssetRepository.java b/src/repository/hsqldb/HSQLDBAssetRepository.java
index 51b37be0..79b2eb7e 100644
--- a/src/repository/hsqldb/HSQLDBAssetRepository.java
+++ b/src/repository/hsqldb/HSQLDBAssetRepository.java
@@ -5,6 +5,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
import data.assets.AssetData;
@@ -23,10 +24,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
// Assets
+ @Override
public AssetData fromAssetId(long assetId) throws DataException {
- try {
- ResultSet resultSet = this.repository
- .checkedExecute("SELECT owner, asset_name, description, quantity, is_divisible, reference FROM Assets WHERE asset_id = ?", assetId);
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT owner, asset_name, description, quantity, is_divisible, reference FROM Assets WHERE asset_id = ?", assetId)) {
if (resultSet == null)
return null;
@@ -43,10 +44,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public AssetData fromAssetName(String assetName) throws DataException {
- try {
- ResultSet resultSet = this.repository
- .checkedExecute("SELECT owner, asset_id, description, quantity, is_divisible, reference FROM Assets WHERE asset_name = ?", assetName);
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT owner, asset_id, description, quantity, is_divisible, reference FROM Assets WHERE asset_name = ?", assetName)) {
if (resultSet == null)
return null;
@@ -63,6 +64,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public boolean assetExists(long assetId) throws DataException {
try {
return this.repository.exists("Assets", "asset_id = ?", assetId);
@@ -71,6 +73,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public boolean assetExists(String assetName) throws DataException {
try {
return this.repository.exists("Assets", "asset_name = ?", assetName);
@@ -79,6 +82,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public void save(AssetData assetData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Assets");
@@ -89,13 +93,21 @@ public class HSQLDBAssetRepository implements AssetRepository {
try {
saveHelper.execute(this.repository);
- if (assetData.getAssetId() == null)
- assetData.setAssetId(this.repository.callIdentity());
+ if (assetData.getAssetId() == null) {
+ // Fetch new assetId
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_id FROM Assets WHERE reference = ?", assetData.getReference())) {
+ if (resultSet == null)
+ throw new DataException("Unable to fetch new asset ID from repository");
+
+ assetData.setAssetId(resultSet.getLong(1));
+ }
+ }
} catch (SQLException e) {
throw new DataException("Unable to save asset into repository", e);
}
}
+ @Override
public void delete(long assetId) throws DataException {
try {
this.repository.delete("Assets", "asset_id = ?", assetId);
@@ -106,11 +118,11 @@ public class HSQLDBAssetRepository implements AssetRepository {
// Orders
+ @Override
public OrderData fromOrderId(byte[] orderId) throws DataException {
- try {
- ResultSet resultSet = this.repository.checkedExecute(
- "SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled FROM AssetOrders WHERE asset_order_id = ?",
- orderId);
+ try (ResultSet resultSet = this.repository.checkedExecute(
+ "SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled FROM AssetOrders WHERE asset_order_id = ?",
+ orderId)) {
if (resultSet == null)
return null;
@@ -120,7 +132,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
BigDecimal amount = resultSet.getBigDecimal(4);
BigDecimal fulfilled = resultSet.getBigDecimal(5);
BigDecimal price = resultSet.getBigDecimal(6);
- long timestamp = resultSet.getTimestamp(7).getTime();
+ long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
boolean isClosed = resultSet.getBoolean(8);
boolean isFulfilled = resultSet.getBoolean(9);
@@ -130,14 +142,14 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public List getOpenOrders(long haveAssetId, long wantAssetId) throws DataException {
List orders = new ArrayList();
- try {
- ResultSet resultSet = this.repository.checkedExecute(
- "SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders "
- + "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE ORDER BY price ASC",
- haveAssetId, wantAssetId);
+ try (ResultSet resultSet = this.repository.checkedExecute(
+ "SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders "
+ + "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE ORDER BY price ASC",
+ haveAssetId, wantAssetId)) {
if (resultSet == null)
return orders;
@@ -147,7 +159,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
BigDecimal amount = resultSet.getBigDecimal(3);
BigDecimal fulfilled = resultSet.getBigDecimal(4);
BigDecimal price = resultSet.getBigDecimal(5);
- long timestamp = resultSet.getTimestamp(6).getTime();
+ long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
boolean isClosed = false;
boolean isFulfilled = false;
@@ -162,6 +174,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public void save(OrderData orderData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetOrders");
@@ -177,6 +190,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public void delete(byte[] orderId) throws DataException {
try {
this.repository.delete("AssetOrders", "asset_order_id = ?", orderId);
@@ -187,12 +201,12 @@ public class HSQLDBAssetRepository implements AssetRepository {
// Trades
+ @Override
public List getOrdersTrades(byte[] initiatingOrderId) throws DataException {
List trades = new ArrayList();
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT target_order_id, amount, price, traded FROM AssetTrades WHERE initiating_order_id = ?",
- initiatingOrderId);
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT target_order_id, amount, price, traded FROM AssetTrades WHERE initiating_order_id = ?", initiatingOrderId)) {
if (resultSet == null)
return trades;
@@ -200,7 +214,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
byte[] targetOrderId = resultSet.getBytes(1);
BigDecimal amount = resultSet.getBigDecimal(2);
BigDecimal price = resultSet.getBigDecimal(3);
- long timestamp = resultSet.getTimestamp(4).getTime();
+ long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
TradeData trade = new TradeData(initiatingOrderId, targetOrderId, amount, price, timestamp);
trades.add(trade);
@@ -212,6 +226,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public void save(TradeData tradeData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetTrades");
@@ -225,6 +240,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
+ @Override
public void delete(TradeData tradeData) throws DataException {
try {
this.repository.delete("AssetTrades", "initiating_order_id = ? AND target_order_id = ? AND amount = ? AND price = ?", tradeData.getInitiator(),
diff --git a/src/repository/hsqldb/HSQLDBBlockRepository.java b/src/repository/hsqldb/HSQLDBBlockRepository.java
index 7fdccc69..6b17aa30 100644
--- a/src/repository/hsqldb/HSQLDBBlockRepository.java
+++ b/src/repository/hsqldb/HSQLDBBlockRepository.java
@@ -5,6 +5,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
import data.block.BlockData;
@@ -25,23 +26,23 @@ public class HSQLDBBlockRepository implements BlockRepository {
this.repository = repository;
}
- private BlockData getBlockFromResultSet(ResultSet rs) throws DataException {
- if (rs == null)
+ private BlockData getBlockFromResultSet(ResultSet resultSet) throws DataException {
+ if (resultSet == null)
return null;
try {
- int version = rs.getInt(1);
- byte[] reference = rs.getBytes(2);
- int transactionCount = rs.getInt(3);
- BigDecimal totalFees = rs.getBigDecimal(4);
- byte[] transactionsSignature = rs.getBytes(5);
- int height = rs.getInt(6);
- long timestamp = rs.getTimestamp(7).getTime();
- BigDecimal generatingBalance = rs.getBigDecimal(8);
- byte[] generatorPublicKey = rs.getBytes(9);
- byte[] generatorSignature = rs.getBytes(10);
- byte[] atBytes = rs.getBytes(11);
- BigDecimal atFees = rs.getBigDecimal(12);
+ int version = resultSet.getInt(1);
+ byte[] reference = resultSet.getBytes(2);
+ int transactionCount = resultSet.getInt(3);
+ BigDecimal totalFees = resultSet.getBigDecimal(4);
+ byte[] transactionsSignature = resultSet.getBytes(5);
+ int height = resultSet.getInt(6);
+ long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
+ BigDecimal generatingBalance = resultSet.getBigDecimal(8);
+ byte[] generatorPublicKey = resultSet.getBytes(9);
+ byte[] generatorSignature = resultSet.getBytes(10);
+ byte[] atBytes = resultSet.getBytes(11);
+ BigDecimal atFees = resultSet.getBigDecimal(12);
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
generatorPublicKey, generatorSignature, atBytes, atFees);
@@ -50,76 +51,77 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
+ @Override
public BlockData fromSignature(byte[] signature) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
- return getBlockFromResultSet(rs);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature)) {
+ return getBlockFromResultSet(resultSet);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
}
}
+ @Override
public BlockData fromReference(byte[] reference) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", reference);
- return getBlockFromResultSet(rs);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", reference)) {
+ return getBlockFromResultSet(resultSet);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
}
}
+ @Override
public BlockData fromHeight(int height) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height);
- return getBlockFromResultSet(rs);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height)) {
+ return getBlockFromResultSet(resultSet);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
}
}
+ @Override
public int getHeightFromSignature(byte[] signature) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature)) {
+ if (resultSet == null)
return 0;
- return rs.getInt(1);
+ return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Error obtaining block height from repository", e);
}
}
+ @Override
public int getBlockchainHeight() throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT MAX(height) FROM Blocks");
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT MAX(height) FROM Blocks")) {
+ if (resultSet == null)
return 0;
- return rs.getInt(1);
+ return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Error obtaining blockchain height from repository", e);
}
}
+ @Override
public BlockData getLastBlock() throws DataException {
return fromHeight(getBlockchainHeight());
}
+ @Override
public List getTransactionsFromSignature(byte[] signature) throws DataException {
List transactions = new ArrayList();
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", signature)) {
+ if (resultSet == null)
return transactions; // No transactions in this block
TransactionRepository transactionRepo = this.repository.getTransactionRepository();
// NB: do-while loop because .checkedExecute() implicitly calls ResultSet.next() for us
do {
- byte[] transactionSignature = rs.getBytes(1);
+ byte[] transactionSignature = resultSet.getBytes(1);
transactions.add(transactionRepo.fromSignature(transactionSignature));
- } while (rs.next());
+ } while (resultSet.next());
} catch (SQLException e) {
throw new DataException(e);
}
@@ -127,6 +129,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
return transactions;
}
+ @Override
public void save(BlockData blockData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks");
@@ -144,6 +147,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
+ @Override
public void delete(BlockData blockData) throws DataException {
try {
this.repository.delete("Blocks", "signature = ?", blockData.getSignature());
@@ -152,6 +156,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
+ @Override
public void save(BlockTransactionData blockTransactionData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("BlockTransactions");
saveHelper.bind("block_signature", blockTransactionData.getBlockSignature()).bind("sequence", blockTransactionData.getSequence())
@@ -164,6 +169,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
+ @Override
public void delete(BlockTransactionData blockTransactionData) throws DataException {
try {
this.repository.delete("BlockTransactions", "block_signature = ? AND sequence = ? AND transaction_signature = ?",
diff --git a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java
index d645fb81..4bb79da5 100644
--- a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java
+++ b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java
@@ -36,14 +36,12 @@ public class HSQLDBDatabaseUpdates {
private static int fetchDatabaseVersion(Connection connection) throws SQLException {
int databaseVersion = 0;
- try {
- Statement stmt = connection.createStatement();
- if (stmt.execute("SELECT version FROM DatabaseInfo")) {
- ResultSet rs = stmt.getResultSet();
-
- if (rs.next())
- databaseVersion = rs.getInt(1);
- }
+ try (Statement stmt = connection.createStatement()) {
+ if (stmt.execute("SELECT version FROM DatabaseInfo"))
+ try (ResultSet resultSet = stmt.getResultSet()) {
+ if (resultSet.next())
+ databaseVersion = resultSet.getInt(1);
+ }
} catch (SQLException e) {
// empty database
}
@@ -60,292 +58,298 @@ public class HSQLDBDatabaseUpdates {
private static boolean databaseUpdating(Connection connection) throws SQLException {
int databaseVersion = fetchDatabaseVersion(connection);
- Statement stmt = connection.createStatement();
+ try (Statement stmt = connection.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 DATABASE COLLATION SQL_TEXT NO PAD");
- stmt.execute("CREATE COLLATION SQL_TEXT_UCC_NO_PAD FOR SQL_TEXT FROM SQL_TEXT_UCC NO PAD");
- stmt.execute("CREATE COLLATION SQL_TEXT_NO_PAD FOR SQL_TEXT FROM SQL_TEXT NO PAD");
- 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_NO_PAD");
- stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
- stmt.execute("CREATE TYPE PollName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
- stmt.execute("CREATE TYPE PollOption AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD");
- stmt.execute("CREATE TYPE PollOptionIndex AS INTEGER");
- stmt.execute("CREATE TYPE DataHash AS VARBINARY(32)");
- stmt.execute("CREATE TYPE AssetID AS BIGINT");
- stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
- stmt.execute("CREATE TYPE AssetOrderID AS VARBINARY(64)");
- stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
- stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
- break;
+ switch (databaseVersion) {
+ case 0:
+ // create from new
+ stmt.execute("SET DATABASE DEFAULT TABLE TYPE CACHED");
+ stmt.execute("SET DATABASE COLLATION SQL_TEXT NO PAD");
+ stmt.execute("CREATE COLLATION SQL_TEXT_UCC_NO_PAD FOR SQL_TEXT FROM SQL_TEXT_UCC NO PAD");
+ stmt.execute("CREATE COLLATION SQL_TEXT_NO_PAD FOR SQL_TEXT FROM SQL_TEXT NO PAD");
+ 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_NO_PAD");
+ stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
+ stmt.execute("CREATE TYPE PollName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
+ stmt.execute("CREATE TYPE PollOption AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD");
+ stmt.execute("CREATE TYPE PollOptionIndex AS INTEGER");
+ stmt.execute("CREATE TYPE DataHash AS VARBINARY(32)");
+ stmt.execute("CREATE TYPE AssetID AS BIGINT");
+ stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
+ stmt.execute("CREATE TYPE AssetOrderID AS VARBINARY(64)");
+ stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
+ stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
+ 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)");
- // For finding blocks by height.
- stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
- // For finding blocks by the account that generated them.
- stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
- // For finding blocks by reference, e.g. child blocks.
- stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
- // Use a separate table space as this table will be very large.
- 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 WITH TIME ZONE NOT NULL, generating_balance QoraAmount NOT NULL, "
+ + "generator QoraPublicKey NOT NULL, generator_signature Signature NOT NULL, AT_data VARBINARY(20000), AT_fees QoraAmount)");
+ // For finding blocks by height.
+ stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
+ // For finding blocks by the account that generated them.
+ stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
+ // For finding blocks by reference, e.g. child blocks.
+ stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
+ // Use a separate table space as this table will be very large.
+ 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)");
- // For finding transactions by transaction type.
- stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
- // For finding transactions using timestamp.
- stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
- // For when a user wants to lookup ALL transactions they have created, regardless of type.
- stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
- // For finding transactions by reference, e.g. child transactions.
- stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
- // Use a separate table space as this table will be very large.
- 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 WITH TIME ZONE NOT NULL, fee QoraAmount NOT NULL, milestone_block BlockSignature)");
+ // For finding transactions by transaction type.
+ stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
+ // For finding transactions using timestamp.
+ stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
+ // For when a user wants to lookup ALL transactions they have created, regardless of type.
+ stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
+ // For finding transactions by reference, e.g. child transactions.
+ stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
+ // Use a separate table space as this table will be very large.
+ 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)");
- // Use a separate table space as this table will be very large.
- 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)");
+ // Use a separate table space as this table will be very large.
+ stmt.execute("SET TABLE BlockTransactions NEW SPACE");
- // Unconfirmed transactions
- // XXX 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
+ // XXX 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 WITH TIME ZONE 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)");
- // Use a separate table space as this table will be very large.
- 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)");
+ // Use a separate table space as this table will be very large.
+ 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, name_reference Signature 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, name_reference Signature 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, name_reference Signature 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, name_reference Signature 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, owner QoraAddress NOT NULL, "
- + "poll_name 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_index TINYINT NOT NULL, option_name PollOption, "
- + "PRIMARY KEY (signature, option_index), 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, owner QoraAddress NOT NULL, "
+ + "poll_name 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_index TINYINT NOT NULL, option_name PollOption, "
+ + "PRIMARY KEY (signature, option_index), 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_name PollName NOT NULL, "
- + "option_index PollOptionIndex NOT NULL, previous_option_index PollOptionIndex, "
- + "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_name PollName NOT NULL, "
+ + "option_index PollOptionIndex NOT NULL, previous_option_index PollOptionIndex, "
+ + "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 QoraAddress 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 QoraAddress 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, sender QoraPublicKey NOT NULL, version TINYINT 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, sender QoraPublicKey NOT NULL, version TINYINT 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_id 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_id 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 QoraAddress 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)");
- // For when a user wants to lookup an asset by name
- 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, owner QoraAddress 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, PRIMARY KEY (asset_id))");
+ // We need a corresponding trigger to make sure new asset_id values are assigned sequentially
+ stmt.execute(
+ "CREATE TRIGGER Asset_ID_Trigger BEFORE INSERT ON Assets REFERENCING NEW ROW AS new_row FOR EACH ROW WHEN (new_row.asset_id IS NULL) "
+ + "SET new_row.asset_id = (SELECT IFNULL(MAX(asset_id) + 1, 0) FROM Assets)");
+ // For when a user wants to lookup an asset by name
+ 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;
- case 23:
- // Asset Orders
- stmt.execute(
- "CREATE TABLE AssetOrders (asset_order_id AssetOrderID, creator QoraPublicKey NOT NULL, have_asset_id AssetID NOT NULL, want_asset_id AssetID NOT NULL, "
- + "amount QoraAmount NOT NULL, fulfilled QoraAmount NOT NULL, price QoraAmount NOT NULL, "
- + "ordered TIMESTAMP NOT NULL, is_closed BOOLEAN NOT NULL, is_fulfilled BOOLEAN NOT NULL, " + "PRIMARY KEY (asset_order_id))");
- // For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
- stmt.execute("CREATE INDEX AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled)");
- // For when a user wants to look up their current/historic orders. is_closed included so user can filter by active/inactive orders.
- stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
- break;
+ case 23:
+ // Asset Orders
+ stmt.execute(
+ "CREATE TABLE AssetOrders (asset_order_id AssetOrderID, creator QoraPublicKey NOT NULL, have_asset_id AssetID NOT NULL, want_asset_id AssetID NOT NULL, "
+ + "amount QoraAmount NOT NULL, fulfilled QoraAmount NOT NULL, price QoraAmount NOT NULL, "
+ + "ordered TIMESTAMP WITH TIME ZONE NOT NULL, is_closed BOOLEAN NOT NULL, is_fulfilled BOOLEAN NOT NULL, "
+ + "PRIMARY KEY (asset_order_id))");
+ // For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
+ stmt.execute("CREATE INDEX AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled)");
+ // For when a user wants to look up their current/historic orders. is_closed included so user can filter by active/inactive orders.
+ stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
+ break;
- case 24:
- // Asset Trades
- stmt.execute("CREATE TABLE AssetTrades (initiating_order_id AssetOrderId NOT NULL, target_order_id AssetOrderId NOT NULL, "
- + "amount QoraAmount NOT NULL, price QoraAmount NOT NULL, traded TIMESTAMP NOT NULL)");
- // For looking up historic trades based on orders
- stmt.execute("CREATE INDEX AssetTradeBuyOrderIndex on AssetTrades (initiating_order_id, traded)");
- stmt.execute("CREATE INDEX AssetTradeSellOrderIndex on AssetTrades (target_order_id, traded)");
- break;
+ case 24:
+ // Asset Trades
+ stmt.execute("CREATE TABLE AssetTrades (initiating_order_id AssetOrderId NOT NULL, target_order_id AssetOrderId NOT NULL, "
+ + "amount QoraAmount NOT NULL, price QoraAmount NOT NULL, traded TIMESTAMP WITH TIME ZONE NOT NULL)");
+ // For looking up historic trades based on orders
+ stmt.execute("CREATE INDEX AssetTradeBuyOrderIndex on AssetTrades (initiating_order_id, traded)");
+ stmt.execute("CREATE INDEX AssetTradeSellOrderIndex on AssetTrades (target_order_id, traded)");
+ break;
- case 25:
- // Polls/Voting
- stmt.execute(
- "CREATE TABLE Polls (poll_name PollName, description VARCHAR(4000) NOT NULL, creator QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
- + "published TIMESTAMP NOT NULL, " + "PRIMARY KEY (poll_name))");
- // Various options available on a poll
- stmt.execute("CREATE TABLE PollOptions (poll_name PollName, option_index TINYINT NOT NULL, option_name PollOption, "
- + "PRIMARY KEY (poll_name, option_index), FOREIGN KEY (poll_name) REFERENCES Polls (poll_name) ON DELETE CASCADE)");
- // Actual votes cast on a poll by voting users. NOTE: only one vote per user supported at this time.
- stmt.execute("CREATE TABLE PollVotes (poll_name PollName, voter QoraPublicKey, option_index PollOptionIndex NOT NULL, "
- + "PRIMARY KEY (poll_name, voter), FOREIGN KEY (poll_name) REFERENCES Polls (poll_name) ON DELETE CASCADE)");
- // For when a user wants to lookup poll they own
- stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
- break;
+ case 25:
+ // Polls/Voting
+ stmt.execute(
+ "CREATE TABLE Polls (poll_name PollName, description VARCHAR(4000) NOT NULL, creator QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
+ + "published TIMESTAMP WITH TIME ZONE NOT NULL, " + "PRIMARY KEY (poll_name))");
+ // Various options available on a poll
+ stmt.execute("CREATE TABLE PollOptions (poll_name PollName, option_index TINYINT NOT NULL, option_name PollOption, "
+ + "PRIMARY KEY (poll_name, option_index), FOREIGN KEY (poll_name) REFERENCES Polls (poll_name) ON DELETE CASCADE)");
+ // Actual votes cast on a poll by voting users. NOTE: only one vote per user supported at this time.
+ stmt.execute("CREATE TABLE PollVotes (poll_name PollName, voter QoraPublicKey, option_index PollOptionIndex NOT NULL, "
+ + "PRIMARY KEY (poll_name, voter), FOREIGN KEY (poll_name) REFERENCES Polls (poll_name) ON DELETE CASCADE)");
+ // For when a user wants to lookup poll they own
+ stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
+ break;
- case 26:
- // Registered Names
- stmt.execute(
- "CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, registrant QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
- + "registered TIMESTAMP NOT NULL, updated TIMESTAMP, reference Signature, is_for_sale BOOLEAN NOT NULL, sale_price QoraAmount, "
- + "PRIMARY KEY (name))");
- break;
+ case 26:
+ // Registered Names
+ stmt.execute(
+ "CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, registrant QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
+ + "registered TIMESTAMP WITH TIME ZONE NOT NULL, updated TIMESTAMP WITH TIME ZONE, reference Signature, is_for_sale BOOLEAN NOT NULL, sale_price QoraAmount, "
+ + "PRIMARY KEY (name))");
+ break;
- default:
- // nothing to do
- return false;
+ default:
+ // nothing to do
+ return false;
+ }
}
// database was updated
diff --git a/src/repository/hsqldb/HSQLDBNameRepository.java b/src/repository/hsqldb/HSQLDBNameRepository.java
index 6737dfd4..6b642ceb 100644
--- a/src/repository/hsqldb/HSQLDBNameRepository.java
+++ b/src/repository/hsqldb/HSQLDBNameRepository.java
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
+import java.util.Calendar;
import data.naming.NameData;
import repository.NameRepository;
@@ -19,19 +20,18 @@ public class HSQLDBNameRepository implements NameRepository {
@Override
public NameData fromName(String name) throws DataException {
- try {
- ResultSet resultSet = this.repository
- .checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name);
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name)) {
if (resultSet == null)
return null;
byte[] registrantPublicKey = resultSet.getBytes(1);
String owner = resultSet.getString(2);
String data = resultSet.getString(3);
- long registered = resultSet.getTimestamp(4).getTime();
+ long registered = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
// Special handling for possibly-NULL "updated" column
- Timestamp updatedTimestamp = resultSet.getTimestamp(5);
+ Timestamp updatedTimestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC));
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
byte[] reference = resultSet.getBytes(6);
diff --git a/src/repository/hsqldb/HSQLDBRepository.java b/src/repository/hsqldb/HSQLDBRepository.java
index ebceafdb..e4c8be7c 100644
--- a/src/repository/hsqldb/HSQLDBRepository.java
+++ b/src/repository/hsqldb/HSQLDBRepository.java
@@ -6,6 +6,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.TimeZone;
import repository.AccountRepository;
import repository.AssetRepository;
@@ -19,6 +20,8 @@ import repository.hsqldb.transaction.HSQLDBTransactionRepository;
public class HSQLDBRepository implements Repository {
+ public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
protected Connection connection;
// NB: no visibility modifier so only callable from within same package
@@ -74,24 +77,22 @@ public class HSQLDBRepository implements Repository {
}
}
- // TODO prevent leaking of connections if .close() is not called before garbage collection of the repository.
- // Maybe use PhantomReference to call .close() on connection after repository destruction?
@Override
public void close() throws DataException {
- try {
+ try (Statement stmt = this.connection.createStatement()) {
// Diagnostic check for uncommitted changes
- Statement stmt = this.connection.createStatement();
if (!stmt.execute("SELECT transaction, transaction_size FROM information_schema.system_sessions")) // TRANSACTION_SIZE() broken?
throw new DataException("Unable to check repository status during close");
- ResultSet rs = stmt.getResultSet();
- if (rs == null || !rs.next())
- throw new DataException("Unable to check repository status during close");
+ try (ResultSet resultSet = stmt.getResultSet()) {
+ if (resultSet == null || !resultSet.next())
+ System.out.println("Unable to check repository status during close");
- boolean inTransaction = rs.getBoolean(1);
- int transactionCount = rs.getInt(2);
- if (inTransaction && transactionCount != 0)
- System.out.println("Uncommitted changes (" + transactionCount + ") during repository close");
+ boolean inTransaction = resultSet.getBoolean(1);
+ int transactionCount = resultSet.getInt(2);
+ if (inTransaction && transactionCount != 0)
+ System.out.println("Uncommitted changes (" + transactionCount + ") during repository close");
+ }
// give connection back to the pool
this.connection.close();
@@ -115,9 +116,12 @@ public class HSQLDBRepository implements Repository {
* @return ResultSet, or null if there are no found rows
* @throws SQLException
*/
+ @SuppressWarnings("resource")
public ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
PreparedStatement preparedStatement = this.connection.prepareStatement(sql);
-
+ // Close the PreparedStatement when the ResultSet is closed otherwise there's a potential resource leak.
+ // We can't use try-with-resources here as closing the PreparedStatement on return would also prematurely close the ResultSet.
+ preparedStatement.closeOnCompletion();
return this.checkedExecuteResultSet(preparedStatement, objects);
}
@@ -198,12 +202,13 @@ public class HSQLDBRepository implements Repository {
* @throws SQLException
*/
public Long callIdentity() throws SQLException {
- PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
- ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement);
- if (resultSet == null)
- return null;
+ try (PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
+ ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement)) {
+ if (resultSet == null)
+ return null;
- return resultSet.getLong(1);
+ return resultSet.getLong(1);
+ }
}
/**
@@ -224,12 +229,13 @@ public class HSQLDBRepository implements Repository {
* @throws SQLException
*/
public boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
- PreparedStatement preparedStatement = this.connection.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " LIMIT 1");
- ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement, objects);
- if (resultSet == null)
- return false;
+ try (PreparedStatement preparedStatement = this.connection.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " LIMIT 1");
+ ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement, objects)) {
+ if (resultSet == null)
+ return false;
- return true;
+ return true;
+ }
}
/**
@@ -241,8 +247,9 @@ public class HSQLDBRepository implements Repository {
* @throws SQLException
*/
public void delete(String tableName, String whereClause, Object... objects) throws SQLException {
- PreparedStatement preparedStatement = this.connection.prepareStatement("DELETE FROM " + tableName + " WHERE " + whereClause);
- this.checkedExecuteUpdateCount(preparedStatement, objects);
+ try (PreparedStatement preparedStatement = this.connection.prepareStatement("DELETE FROM " + tableName + " WHERE " + whereClause)) {
+ this.checkedExecuteUpdateCount(preparedStatement, objects);
+ }
}
}
diff --git a/src/repository/hsqldb/HSQLDBRepositoryFactory.java b/src/repository/hsqldb/HSQLDBRepositoryFactory.java
index b8c2dc99..b5e368bc 100644
--- a/src/repository/hsqldb/HSQLDBRepositoryFactory.java
+++ b/src/repository/hsqldb/HSQLDBRepositoryFactory.java
@@ -3,6 +3,7 @@ package repository.hsqldb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
+import java.util.Properties;
import org.hsqldb.jdbc.JDBCPool;
@@ -22,6 +23,14 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
this.connectionPool = new JDBCPool();
this.connectionPool.setUrl(this.connectionUrl);
+ Properties properties = new Properties();
+ properties.setProperty("close_result", "true"); // Auto-close old ResultSet if Statement creates new ResultSet
+ properties.setProperty("sql.strict_exec", "true"); // No multi-SQL execute() or DDL/DML executeQuery()
+ properties.setProperty("sql.enforce_names", "true"); // SQL keywords cannot be used as DB object names, e.g. table names
+ properties.setProperty("sql.syntax_mys", "true"); // Required for our use of INSERT ... ON DUPLICATE KEY UPDATE ... syntax
+ properties.setProperty("sql.pad_space", "false"); // Do not pad strings to same length before comparison
+ this.connectionPool.setProperties(properties);
+
// Perform DB updates?
try (final Connection connection = this.connectionPool.getConnection()) {
HSQLDBDatabaseUpdates.updateDatabase(connection);
@@ -30,6 +39,7 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
}
}
+ @Override
public Repository getRepository() throws DataException {
try {
return new HSQLDBRepository(this.getConnection());
@@ -41,22 +51,23 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
private Connection getConnection() throws SQLException {
Connection connection = this.connectionPool.getConnection();
- // start transaction
+ // Set transaction level
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
connection.setAutoCommit(false);
return connection;
}
+ @Override
public void close() throws DataException {
try {
// Close all existing connections immediately
this.connectionPool.close(0);
// Now that all connections are closed, create a dedicated connection to shut down repository
- Connection connection = DriverManager.getConnection(this.connectionUrl);
- connection.createStatement().execute("SHUTDOWN");
- connection.close();
+ try (Connection connection = DriverManager.getConnection(this.connectionUrl)) {
+ connection.createStatement().execute("SHUTDOWN");
+ }
} catch (SQLException e) {
throw new DataException("Error during repository shutdown", e);
}
diff --git a/src/repository/hsqldb/HSQLDBSaver.java b/src/repository/hsqldb/HSQLDBSaver.java
index 6c81be29..8b05767a 100644
--- a/src/repository/hsqldb/HSQLDBSaver.java
+++ b/src/repository/hsqldb/HSQLDBSaver.java
@@ -3,8 +3,10 @@ package repository.hsqldb;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.List;
/**
@@ -56,11 +58,11 @@ public class HSQLDBSaver {
*/
public boolean execute(HSQLDBRepository repository) throws SQLException {
String sql = this.formatInsertWithPlaceholders();
- PreparedStatement preparedStatement = repository.connection.prepareStatement(sql);
+ try (PreparedStatement preparedStatement = repository.connection.prepareStatement(sql)) {
+ this.bindValues(preparedStatement);
- this.bindValues(preparedStatement);
-
- return preparedStatement.execute();
+ return preparedStatement.execute();
+ }
}
/**
@@ -107,11 +109,15 @@ public class HSQLDBSaver {
for (int i = 0; i < this.objects.size(); ++i) {
Object object = this.objects.get(i);
- // Special treatment for BigDecimals so that they retain their "scale",
- // which would otherwise be assumed as 0.
if (object instanceof BigDecimal) {
+ // Special treatment for BigDecimals so that they retain their "scale",
+ // which would otherwise be assumed as 0.
preparedStatement.setBigDecimal(i + 1, (BigDecimal) object);
preparedStatement.setBigDecimal(i + this.objects.size() + 1, (BigDecimal) object);
+ } else if (object instanceof Timestamp) {
+ // Special treatment for Timestamps so that they are stored as UTC
+ preparedStatement.setTimestamp(i + 1, (Timestamp) object, Calendar.getInstance(HSQLDBRepository.UTC));
+ preparedStatement.setTimestamp(i + this.objects.size() + 1, (Timestamp) object, Calendar.getInstance(HSQLDBRepository.UTC));
} else {
preparedStatement.setObject(i + 1, object);
preparedStatement.setObject(i + this.objects.size() + 1, object);
diff --git a/src/repository/hsqldb/HSQLDBVotingRepository.java b/src/repository/hsqldb/HSQLDBVotingRepository.java
index de929878..cedb1c62 100644
--- a/src/repository/hsqldb/HSQLDBVotingRepository.java
+++ b/src/repository/hsqldb/HSQLDBVotingRepository.java
@@ -4,6 +4,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
import data.voting.PollData;
@@ -22,36 +23,39 @@ public class HSQLDBVotingRepository implements VotingRepository {
// Polls
+ @Override
public PollData fromPollName(String pollName) throws DataException {
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT description, creator, owner, published FROM Polls WHERE poll_name = ?", pollName);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT description, creator, owner, published FROM Polls WHERE poll_name = ?", pollName)) {
if (resultSet == null)
return null;
String description = resultSet.getString(1);
byte[] creatorPublicKey = resultSet.getBytes(2);
String owner = resultSet.getString(3);
- long published = resultSet.getTimestamp(4).getTime();
+ long published = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
- resultSet = this.repository.checkedExecute("SELECT option_name FROM PollOptions where poll_name = ? ORDER BY option_index ASC", pollName);
- if (resultSet == null)
- return null;
+ try (ResultSet optionsResultSet = this.repository
+ .checkedExecute("SELECT option_name FROM PollOptions where poll_name = ? ORDER BY option_index ASC", pollName)) {
+ if (optionsResultSet == null)
+ return null;
- List pollOptions = new ArrayList();
+ List pollOptions = new ArrayList();
- // NOTE: do-while because checkedExecute() above has already called rs.next() for us
- do {
- String optionName = resultSet.getString(1);
+ // NOTE: do-while because checkedExecute() above has already called rs.next() for us
+ do {
+ String optionName = optionsResultSet.getString(1);
- pollOptions.add(new PollOptionData(optionName));
- } while (resultSet.next());
+ pollOptions.add(new PollOptionData(optionName));
+ } while (optionsResultSet.next());
- return new PollData(creatorPublicKey, owner, pollName, description, pollOptions, published);
+ return new PollData(creatorPublicKey, owner, pollName, description, pollOptions, published);
+ }
} catch (SQLException e) {
throw new DataException("Unable to fetch poll from repository", e);
}
}
+ @Override
public boolean pollExists(String pollName) throws DataException {
try {
return this.repository.exists("Polls", "poll_name = ?", pollName);
@@ -60,6 +64,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
}
}
+ @Override
public void save(PollData pollData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Polls");
@@ -89,6 +94,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
}
}
+ @Override
public void delete(String pollName) throws DataException {
// NOTE: The corresponding rows in PollOptions are deleted automatically by the database thanks to "ON DELETE CASCADE" in the PollOptions' FOREIGN KEY
// definition.
@@ -101,11 +107,11 @@ public class HSQLDBVotingRepository implements VotingRepository {
// Votes
+ @Override
public List getVotes(String pollName) throws DataException {
List votes = new ArrayList();
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT voter, option_index FROM PollVotes WHERE poll_name = ?", pollName);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT voter, option_index FROM PollVotes WHERE poll_name = ?", pollName)) {
if (resultSet == null)
return votes;
@@ -123,10 +129,10 @@ public class HSQLDBVotingRepository implements VotingRepository {
}
}
+ @Override
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException {
- try {
- ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
- voterPublicKey);
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
+ voterPublicKey)) {
if (resultSet == null)
return null;
@@ -138,6 +144,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
}
}
+ @Override
public void save(VoteOnPollData voteOnPollData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("PollVotes");
@@ -151,6 +158,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
}
}
+ @Override
public void delete(String pollName, byte[] voterPublicKey) throws DataException {
try {
this.repository.delete("PollVotes", "poll_name = ? AND voter = ?", pollName, voterPublicKey);
diff --git a/src/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java
index f6067e77..eabb1559 100644
--- a/src/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBArbitraryTransactionRepository.java
@@ -20,16 +20,15 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT sender, version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
- signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
+ signature)) {
+ if (resultSet == null)
return null;
- byte[] senderPublicKey = rs.getBytes(1);
- int version = rs.getInt(2);
- int service = rs.getInt(3);
- byte[] dataHash = rs.getBytes(4);
+ byte[] senderPublicKey = resultSet.getBytes(1);
+ int version = resultSet.getInt(2);
+ int service = resultSet.getInt(3);
+ byte[] dataHash = resultSet.getBytes(4);
List payments = this.getPaymentsFromSignature(signature);
@@ -51,7 +50,8 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
HSQLDBSaver saveHelper = new HSQLDBSaver("ArbitraryTransactions");
saveHelper.bind("signature", arbitraryTransactionData.getSignature()).bind("sender", arbitraryTransactionData.getSenderPublicKey())
- .bind("version", arbitraryTransactionData.getVersion()).bind("service", arbitraryTransactionData.getService()).bind("data_hash", arbitraryTransactionData.getData());
+ .bind("version", arbitraryTransactionData.getVersion()).bind("service", arbitraryTransactionData.getService())
+ .bind("data_hash", arbitraryTransactionData.getData());
try {
saveHelper.execute(this.repository);
diff --git a/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java
index 1daef638..e2393d9d 100644
--- a/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java
@@ -17,16 +17,15 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] buyerPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?",
- signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?",
+ signature)) {
+ if (resultSet == null)
return null;
- String name = rs.getString(1);
- BigDecimal amount = rs.getBigDecimal(2);
- String seller = rs.getString(3);
- byte[] nameReference = rs.getBytes(4);
+ String name = resultSet.getString(1);
+ BigDecimal amount = resultSet.getBigDecimal(2);
+ String seller = resultSet.getString(3);
+ byte[] nameReference = resultSet.getBytes(4);
return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBCancelOrderTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBCancelOrderTransactionRepository.java
index 47f5aa6f..45ffcab2 100644
--- a/src/repository/hsqldb/transaction/HSQLDBCancelOrderTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBCancelOrderTransactionRepository.java
@@ -17,12 +17,11 @@ public class HSQLDBCancelOrderTransactionRepository extends HSQLDBTransactionRep
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT asset_order_id FROM CancelAssetOrderTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_order_id FROM CancelAssetOrderTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- byte[] assetOrderId = rs.getBytes(1);
+ byte[] assetOrderId = resultSet.getBytes(1);
return new CancelOrderTransactionData(creatorPublicKey, assetOrderId, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java
index e8d09223..b54641a2 100644
--- a/src/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBCancelSellNameTransactionRepository.java
@@ -17,12 +17,11 @@ public class HSQLDBCancelSellNameTransactionRepository extends HSQLDBTransaction
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT name FROM CancelSellNameTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT name FROM CancelSellNameTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String name = rs.getString(1);
+ String name = resultSet.getString(1);
return new CancelSellNameTransactionData(ownerPublicKey, name, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBCreateOrderTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBCreateOrderTransactionRepository.java
index 952b020f..06bd37d6 100644
--- a/src/repository/hsqldb/transaction/HSQLDBCreateOrderTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBCreateOrderTransactionRepository.java
@@ -17,16 +17,15 @@ public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRep
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository
- .checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- long haveAssetId = rs.getLong(1);
- BigDecimal amount = rs.getBigDecimal(2);
- long wantAssetId = rs.getLong(3);
- BigDecimal price = rs.getBigDecimal(4);
+ long haveAssetId = resultSet.getLong(1);
+ BigDecimal amount = resultSet.getBigDecimal(2);
+ long wantAssetId = resultSet.getLong(3);
+ BigDecimal price = resultSet.getBigDecimal(4);
return new CreateOrderTransactionData(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java
index 7086fe46..08d8dc4e 100644
--- a/src/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBCreatePollTransactionRepository.java
@@ -20,30 +20,31 @@ public class HSQLDBCreatePollTransactionRepository extends HSQLDBTransactionRepo
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT owner, poll_name, description FROM CreatePollTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, poll_name, description FROM CreatePollTransactions WHERE signature = ?",
+ signature)) {
+ if (resultSet == null)
return null;
- String owner = rs.getString(1);
- String pollName = rs.getString(2);
- String description = rs.getString(3);
+ String owner = resultSet.getString(1);
+ String pollName = resultSet.getString(2);
+ String description = resultSet.getString(3);
- rs = this.repository.checkedExecute("SELECT option_name FROM CreatePollTransactionOptions where signature = ? ORDER BY option_index ASC",
- signature);
- if (rs == null)
- return null;
+ try (ResultSet optionsResultSet = this.repository
+ .checkedExecute("SELECT option_name FROM CreatePollTransactionOptions where signature = ? ORDER BY option_index ASC", signature)) {
+ if (optionsResultSet == null)
+ return null;
- List pollOptions = new ArrayList();
+ List pollOptions = new ArrayList();
- // NOTE: do-while because checkedExecute() above has already called rs.next() for us
- do {
- String optionName = rs.getString(1);
+ // NOTE: do-while because checkedExecute() above has already called rs.next() for us
+ do {
+ String optionName = optionsResultSet.getString(1);
- pollOptions.add(new PollOptionData(optionName));
- } while (rs.next());
+ pollOptions.add(new PollOptionData(optionName));
+ } while (optionsResultSet.next());
- return new CreatePollTransactionData(creatorPublicKey, owner, pollName, description, pollOptions, fee, timestamp, reference, signature);
+ return new CreatePollTransactionData(creatorPublicKey, owner, pollName, description, pollOptions, fee, timestamp, reference, signature);
+ }
} catch (SQLException e) {
throw new DataException("Unable to fetch create poll transaction from repository", e);
}
diff --git a/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java
index 3f5e9e0e..f285f02a 100644
--- a/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBGenesisTransactionRepository.java
@@ -17,13 +17,12 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String recipient = rs.getString(1);
- BigDecimal amount = rs.getBigDecimal(2).setScale(8);
+ String recipient = resultSet.getString(1);
+ BigDecimal amount = resultSet.getBigDecimal(2).setScale(8);
return new GenesisTransactionData(recipient, amount, timestamp, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java
index 90e37f7f..ad417a95 100644
--- a/src/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBIssueAssetTransactionRepository.java
@@ -17,20 +17,18 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute(
- "SELECT issuer, owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?",
- signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute(
+ "SELECT issuer, owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- byte[] issuerPublicKey = rs.getBytes(1);
- String owner = rs.getString(2);
- String assetName = rs.getString(3);
- String description = rs.getString(4);
- long quantity = rs.getLong(5);
- boolean isDivisible = rs.getBoolean(6);
- Long assetId = rs.getLong(7);
+ byte[] issuerPublicKey = resultSet.getBytes(1);
+ String owner = resultSet.getString(2);
+ String assetName = resultSet.getString(3);
+ String description = resultSet.getString(4);
+ long quantity = resultSet.getLong(5);
+ boolean isDivisible = resultSet.getBoolean(6);
+ Long assetId = resultSet.getLong(7);
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
signature);
diff --git a/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java
index aa9ee082..7bfd964b 100644
--- a/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java
@@ -17,20 +17,19 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute(
- "SELECT version, sender, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute(
+ "SELECT version, sender, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- int version = rs.getInt(1);
- byte[] senderPublicKey = rs.getBytes(2);
- String recipient = rs.getString(3);
- boolean isText = rs.getBoolean(4);
- boolean isEncrypted = rs.getBoolean(5);
- BigDecimal amount = rs.getBigDecimal(6);
- Long assetId = rs.getLong(7);
- byte[] data = rs.getBytes(8);
+ int version = resultSet.getInt(1);
+ byte[] senderPublicKey = resultSet.getBytes(2);
+ String recipient = resultSet.getString(3);
+ boolean isText = resultSet.getBoolean(4);
+ boolean isEncrypted = resultSet.getBoolean(5);
+ BigDecimal amount = resultSet.getBigDecimal(6);
+ Long assetId = resultSet.getLong(7);
+ byte[] data = resultSet.getBytes(8);
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
signature);
diff --git a/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java
index 539cda3c..9db37966 100644
--- a/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java
@@ -19,12 +19,11 @@ public class HSQLDBMultiPaymentTransactionRepository extends HSQLDBTransactionRe
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- byte[] senderPublicKey = rs.getBytes(1);
+ byte[] senderPublicKey = resultSet.getBytes(1);
List payments = this.getPaymentsFromSignature(signature);
diff --git a/src/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java
index e5222a21..059684f8 100644
--- a/src/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBPaymentTransactionRepository.java
@@ -17,14 +17,13 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- byte[] senderPublicKey = rs.getBytes(1);
- String recipient = rs.getString(2);
- BigDecimal amount = rs.getBigDecimal(3);
+ byte[] senderPublicKey = resultSet.getBytes(1);
+ String recipient = resultSet.getString(2);
+ BigDecimal amount = resultSet.getBigDecimal(3);
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java
index 3c11b0b3..86c54c9c 100644
--- a/src/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java
@@ -17,14 +17,13 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] registrantPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String owner = rs.getString(1);
- String name = rs.getString(2);
- String data = rs.getString(3);
+ String owner = resultSet.getString(1);
+ String name = resultSet.getString(2);
+ String data = resultSet.getString(3);
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java
index 7669f0da..f6628d47 100644
--- a/src/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBSellNameTransactionRepository.java
@@ -17,13 +17,12 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT name, amount FROM SellNameTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount FROM SellNameTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String name = rs.getString(1);
- BigDecimal amount = rs.getBigDecimal(2);
+ String name = resultSet.getString(1);
+ BigDecimal amount = resultSet.getBigDecimal(2);
return new SellNameTransactionData(ownerPublicKey, name, amount, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java
index 29f891f2..f6d7fc59 100644
--- a/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java
@@ -5,6 +5,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
import data.PaymentData;
@@ -59,17 +60,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
protected HSQLDBTransactionRepository() {
}
+ @Override
public TransactionData fromSignature(byte[] signature) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?",
+ signature)) {
+ if (resultSet == null)
return null;
- TransactionType type = TransactionType.valueOf(rs.getInt(1));
- byte[] reference = rs.getBytes(2);
- byte[] creatorPublicKey = rs.getBytes(3);
- long timestamp = rs.getTimestamp(4).getTime();
- BigDecimal fee = rs.getBigDecimal(5).setScale(8);
+ TransactionType type = TransactionType.valueOf(resultSet.getInt(1));
+ byte[] reference = resultSet.getBytes(2);
+ byte[] creatorPublicKey = resultSet.getBytes(3);
+ long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
+ BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
} catch (SQLException e) {
@@ -77,17 +79,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
}
+ @Override
public TransactionData fromReference(byte[] reference) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?", reference);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?",
+ reference)) {
+ if (resultSet == null)
return null;
- TransactionType type = TransactionType.valueOf(rs.getInt(1));
- byte[] signature = rs.getBytes(2);
- byte[] creatorPublicKey = rs.getBytes(3);
- long timestamp = rs.getTimestamp(4).getTime();
- BigDecimal fee = rs.getBigDecimal(5).setScale(8);
+ TransactionType type = TransactionType.valueOf(resultSet.getInt(1));
+ byte[] signature = resultSet.getBytes(2);
+ byte[] creatorPublicKey = resultSet.getBytes(3);
+ long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
+ BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
} catch (SQLException e) {
@@ -152,21 +155,21 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
}
protected List getPaymentsFromSignature(byte[] signature) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM SharedTransactionPayments WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM SharedTransactionPayments WHERE signature = ?",
+ signature)) {
+ if (resultSet == null)
return null;
List payments = new ArrayList();
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
do {
- String recipient = rs.getString(1);
- BigDecimal amount = rs.getBigDecimal(2);
- long assetId = rs.getLong(3);
+ String recipient = resultSet.getString(1);
+ BigDecimal amount = resultSet.getBigDecimal(2);
+ long assetId = resultSet.getLong(3);
payments.add(new PaymentData(recipient, assetId, amount));
- } while (rs.next());
+ } while (resultSet.next());
return payments;
} catch (SQLException e) {
@@ -194,16 +197,15 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
if (signature == null)
return 0;
- // in one go?
- try {
- ResultSet rs = this.repository.checkedExecute(
- "SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
- signature);
+ // Fetch height using join via block's transactions
+ try (ResultSet resultSet = this.repository.checkedExecute(
+ "SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
+ signature)) {
- if (rs == null)
+ if (resultSet == null)
return 0;
- return rs.getInt(1);
+ return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch transaction's height from repository", e);
}
@@ -215,12 +217,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
return null;
// Fetch block signature (if any)
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1",
+ signature)) {
+ if (resultSet == null)
return null;
- byte[] blockSignature = rs.getBytes(1);
+ byte[] blockSignature = resultSet.getBytes(1);
return this.repository.getBlockRepository().fromSignature(blockSignature);
} catch (SQLException | DataException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java
index 5272155d..6c0224b4 100644
--- a/src/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java
@@ -17,16 +17,15 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT sender, recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?",
- signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT sender, recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- byte[] senderPublicKey = rs.getBytes(1);
- String recipient = rs.getString(2);
- long assetId = rs.getLong(3);
- BigDecimal amount = rs.getBigDecimal(4);
+ byte[] senderPublicKey = resultSet.getBytes(1);
+ String recipient = resultSet.getString(2);
+ long assetId = resultSet.getLong(3);
+ BigDecimal amount = resultSet.getBigDecimal(4);
return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java
index 11f0cb9f..dfafa804 100644
--- a/src/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java
@@ -17,15 +17,15 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository.checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String newOwner = rs.getString(1);
- String name = rs.getString(2);
- String newData = rs.getString(3);
- byte[] nameReference = rs.getBytes(4);
+ String newOwner = resultSet.getString(1);
+ String name = resultSet.getString(2);
+ String newData = resultSet.getString(3);
+ byte[] nameReference = resultSet.getBytes(4);
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java
index aedf1ffc..baeb5125 100644
--- a/src/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java
+++ b/src/repository/hsqldb/transaction/HSQLDBVoteOnPollTransactionRepository.java
@@ -17,15 +17,14 @@ public class HSQLDBVoteOnPollTransactionRepository extends HSQLDBTransactionRepo
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
- try {
- ResultSet rs = this.repository
- .checkedExecute("SELECT poll_name, option_index, previous_option_index FROM VoteOnPollTransactions WHERE signature = ?", signature);
- if (rs == null)
+ try (ResultSet resultSet = this.repository
+ .checkedExecute("SELECT poll_name, option_index, previous_option_index FROM VoteOnPollTransactions WHERE signature = ?", signature)) {
+ if (resultSet == null)
return null;
- String pollName = rs.getString(1);
- int optionIndex = rs.getInt(2);
- Integer previousOptionIndex = rs.getInt(3);
+ String pollName = resultSet.getString(1);
+ int optionIndex = resultSet.getInt(2);
+ Integer previousOptionIndex = resultSet.getInt(3);
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
} catch (SQLException e) {
diff --git a/src/test/Common.java b/src/test/Common.java
index fc55c4a3..e3734218 100644
--- a/src/test/Common.java
+++ b/src/test/Common.java
@@ -10,7 +10,8 @@ import repository.hsqldb.HSQLDBRepositoryFactory;
public class Common {
- public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true;sql.pad_space=false";
+ // public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true;sql.pad_space=false";
+ public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
@BeforeClass
public static void setRepository() throws DataException {
diff --git a/src/test/RepositoryTests.java b/src/test/RepositoryTests.java
index 5cc3e3c1..e2df05ec 100644
--- a/src/test/RepositoryTests.java
+++ b/src/test/RepositoryTests.java
@@ -35,15 +35,16 @@ public class RepositoryTests extends Common {
@Test
public void testAccessAfterClose() throws DataException {
- Repository repository = RepositoryManager.getRepository();
- assertNotNull(repository);
+ try (Repository repository = RepositoryManager.getRepository()) {
+ assertNotNull(repository);
- repository.close();
+ repository.close();
- try {
- repository.discardChanges();
- fail();
- } catch (NullPointerException | DataException e) {
+ try {
+ repository.discardChanges();
+ fail();
+ } catch (NullPointerException | DataException e) {
+ }
}
}
diff --git a/src/transform/PaymentTransformer.java b/src/transform/PaymentTransformer.java
index b664cd43..ef3da0a2 100644
--- a/src/transform/PaymentTransformer.java
+++ b/src/transform/PaymentTransformer.java
@@ -18,17 +18,16 @@ public class PaymentTransformer extends Transformer {
// Property lengths
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
private static final int ASSET_ID_LENGTH = LONG_LENGTH;
- private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH;
+ private static final int AMOUNT_LENGTH = 12;
private static final int TOTAL_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH;
public static PaymentData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
- if (byteBuffer.remaining() < TOTAL_LENGTH)
- throw new TransformationException("Byte data too short for payment information");
-
String recipient = Serialization.deserializeAddress(byteBuffer);
+
long assetId = byteBuffer.getLong();
- BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
+
+ BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH);
return new PaymentData(recipient, assetId, amount);
}
@@ -42,8 +41,10 @@ public class PaymentTransformer extends Transformer {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
Serialization.serializeAddress(bytes, paymentData.getRecipient());
+
bytes.write(Longs.toByteArray(paymentData.getAssetId()));
- Serialization.serializeBigDecimal(bytes, paymentData.getAmount());
+
+ Serialization.serializeBigDecimal(bytes, paymentData.getAmount(), AMOUNT_LENGTH);
return bytes.toByteArray();
} catch (IOException | ClassCastException e) {
diff --git a/src/transform/block/BlockTransformer.java b/src/transform/block/BlockTransformer.java
index e4587da5..0542948a 100644
--- a/src/transform/block/BlockTransformer.java
+++ b/src/transform/block/BlockTransformer.java
@@ -79,10 +79,12 @@ public class BlockTransformer extends Transformer {
byteBuffer.get(reference);
BigDecimal generatingBalance = BigDecimal.valueOf(byteBuffer.getLong()).setScale(8);
+
byte[] generatorPublicKey = Serialization.deserializePublicKey(byteBuffer);
byte[] transactionsSignature = new byte[TRANSACTIONS_SIGNATURE_LENGTH];
byteBuffer.get(transactionsSignature);
+
byte[] generatorSignature = new byte[GENERATOR_SIGNATURE_LENGTH];
byteBuffer.get(generatorSignature);
@@ -105,13 +107,16 @@ public class BlockTransformer extends Transformer {
// Parse transactions now, compared to deferred parsing in Gen1, so we can throw ParseException if need be.
List transactions = new ArrayList();
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
+
for (int t = 0; t < transactionCount; ++t) {
if (byteBuffer.remaining() < TRANSACTION_SIZE_LENGTH)
throw new TransformationException("Byte data too short for Block Transaction length");
int transactionLength = byteBuffer.getInt();
+
if (byteBuffer.remaining() < transactionLength)
throw new TransformationException("Byte data too short for Block Transaction");
+
if (transactionLength > Block.MAX_BLOCK_BYTES)
throw new TransformationException("Byte data too long for Block Transaction");
@@ -293,7 +298,7 @@ public class BlockTransformer extends Transformer {
for (Transaction transaction : block.getTransactions()) {
if (!transaction.isSignatureValid())
- return null;
+ throw new TransformationException("Transaction signature invalid when building block's transactions signature");
bytes.write(transaction.getTransactionData().getSignature());
}
diff --git a/src/transform/transaction/ArbitraryTransactionTransformer.java b/src/transform/transaction/ArbitraryTransactionTransformer.java
index 1f46dac5..5d408a34 100644
--- a/src/transform/transaction/ArbitraryTransactionTransformer.java
+++ b/src/transform/transaction/ArbitraryTransactionTransformer.java
@@ -5,6 +5,7 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.json.simple.JSONArray;
@@ -16,6 +17,7 @@ import com.google.common.primitives.Longs;
import data.transaction.TransactionData;
import qora.account.PublicKeyAccount;
+import qora.block.BlockChain;
import qora.transaction.ArbitraryTransaction;
import data.PaymentData;
import data.transaction.ArbitraryTransactionData;
@@ -120,6 +122,34 @@ public class ArbitraryTransactionTransformer extends TransactionTransformer {
}
}
+ /**
+ * In Qora v1, the bytes used for verification are really mangled so we need to test for v1-ness and adjust the bytes accordingly.
+ *
+ * @param transactionData
+ * @return byte[]
+ * @throws TransformationException
+ */
+ public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
+ ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) transactionData;
+ byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
+
+ if (arbitraryTransactionData.getVersion() == 1 || transactionData.getTimestamp() >= BlockChain.getArbitraryTransactionV2Timestamp())
+ return bytes;
+
+ // Special v1 version
+
+ // In v1, a coding error means that all bytes prior to final payment entry are lost!
+ // If there are no payments then we can skip mangling
+ if (arbitraryTransactionData.getPayments().size() == 0)
+ return bytes;
+
+ // So we're left with: final payment entry, service, data size, data, fee
+ int v1Length = PaymentTransformer.getDataLength() + SERVICE_LENGTH + DATA_SIZE_LENGTH + arbitraryTransactionData.getData().length + FEE_LENGTH;
+ int v1Start = bytes.length - v1Length;
+
+ return Arrays.copyOfRange(bytes, v1Start, bytes.length);
+ }
+
@SuppressWarnings("unchecked")
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
diff --git a/src/transform/transaction/CreatePollTransactionTransformer.java b/src/transform/transaction/CreatePollTransactionTransformer.java
index de18d78a..c29ad508 100644
--- a/src/transform/transaction/CreatePollTransactionTransformer.java
+++ b/src/transform/transaction/CreatePollTransactionTransformer.java
@@ -155,7 +155,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
// Special v1 version
// Replace transaction type with incorrect Register Name value
- System.arraycopy(Ints.toByteArray(TransactionType.REGISTER_NAME.value), 0, bytes, 0, TransactionTransformer.INT_LENGTH);
+ System.arraycopy(Ints.toByteArray(TransactionType.REGISTER_NAME.value), 0, bytes, 0, INT_LENGTH);
System.out.println(HashCode.fromBytes(bytes).toString());
diff --git a/src/transform/transaction/IssueAssetTransactionTransformer.java b/src/transform/transaction/IssueAssetTransactionTransformer.java
index dd4bf959..15b873e6 100644
--- a/src/transform/transaction/IssueAssetTransactionTransformer.java
+++ b/src/transform/transaction/IssueAssetTransactionTransformer.java
@@ -131,8 +131,8 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
// Special v1 version
// Zero duplicate signature/reference
- int start = bytes.length - TransactionTransformer.SIGNATURE_LENGTH - TransactionTransformer.BIG_DECIMAL_LENGTH;
- int end = start + TransactionTransformer.SIGNATURE_LENGTH;
+ int start = bytes.length - SIGNATURE_LENGTH - BIG_DECIMAL_LENGTH;
+ int end = start + SIGNATURE_LENGTH;
Arrays.fill(bytes, start, end, (byte) 0);
return bytes;
diff --git a/src/transform/transaction/MultiPaymentTransactionTransformer.java b/src/transform/transaction/MultiPaymentTransactionTransformer.java
index a143ab31..470b1866 100644
--- a/src/transform/transaction/MultiPaymentTransactionTransformer.java
+++ b/src/transform/transaction/MultiPaymentTransactionTransformer.java
@@ -5,6 +5,7 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.json.simple.JSONArray;
@@ -16,6 +17,7 @@ import com.google.common.primitives.Longs;
import data.transaction.TransactionData;
import qora.account.PublicKeyAccount;
+import qora.block.BlockChain;
import data.PaymentData;
import data.transaction.MultiPaymentTransactionData;
import transform.PaymentTransformer;
@@ -87,6 +89,29 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer {
}
}
+ /**
+ * In Qora v1, the bytes used for verification are really mangled so we need to test for v1-ness and adjust the bytes accordingly.
+ *
+ * @param transactionData
+ * @return byte[]
+ * @throws TransformationException
+ */
+ public static byte[] toBytesForSigningImpl(TransactionData transactionData) throws TransformationException {
+ byte[] bytes = TransactionTransformer.toBytesForSigningImpl(transactionData);
+
+ if (transactionData.getTimestamp() >= BlockChain.getIssueAssetV2Timestamp())
+ return bytes;
+
+ // Special v1 version
+
+ // In v1, a coding error means that all bytes prior to final payment entry are lost!
+ // So we're left with: final payment entry and fee. Signature has already been stripped
+ int v1Length = PaymentTransformer.getDataLength() + FEE_LENGTH;
+ int v1Start = bytes.length - v1Length;
+
+ return Arrays.copyOfRange(bytes, v1Start, bytes.length);
+ }
+
@SuppressWarnings("unchecked")
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
diff --git a/src/v1feeder.java b/src/v1feeder.java
index 6c6b0d63..d8c05e45 100644
--- a/src/v1feeder.java
+++ b/src/v1feeder.java
@@ -36,27 +36,27 @@ public class v1feeder extends Thread {
private static final int DEFAULT_PORT = 9084;
private static final int MAGIC_LENGTH = 4;
- private static final int TYPE_LENGTH = 4;
+ // private static final int TYPE_LENGTH = 4;
private static final int HAS_ID_LENGTH = 1;
- private static final int ID_LENGTH = 4;
- private static final int DATA_SIZE_LENGTH = 4;
+ // private static final int ID_LENGTH = 4;
+ // private static final int DATA_SIZE_LENGTH = 4;
private static final int CHECKSUM_LENGTH = 4;
private static final int SIGNATURE_LENGTH = 128;
private static final byte[] MAINNET_MAGIC = { 0x12, 0x34, 0x56, 0x78 };
- private static final int GET_PEERS_TYPE = 1;
- private static final int PEERS_TYPE = 2;
+ // private static final int GET_PEERS_TYPE = 1;
+ // private static final int PEERS_TYPE = 2;
private static final int HEIGHT_TYPE = 3;
private static final int GET_SIGNATURES_TYPE = 4;
private static final int SIGNATURES_TYPE = 5;
private static final int GET_BLOCK_TYPE = 6;
private static final int BLOCK_TYPE = 7;
- private static final int TRANSACTION_TYPE = 8;
+ // private static final int TRANSACTION_TYPE = 8;
private static final int PING_TYPE = 9;
private static final int VERSION_TYPE = 10;
- private static final int FIND_MYSELF_TYPE = 11;
+ // private static final int FIND_MYSELF_TYPE = 11;
private Socket socket;
private OutputStream out;
@@ -237,7 +237,7 @@ public class v1feeder extends Thread {
break;
case VERSION_TYPE:
- long timestamp = byteBuffer.getLong();
+ @SuppressWarnings("unused") long timestamp = byteBuffer.getLong();
int versionLength = byteBuffer.getInt();
byte[] versionBytes = new byte[versionLength];
byteBuffer.get(versionBytes);
@@ -299,6 +299,7 @@ public class v1feeder extends Thread {
return newBufferEnd;
}
+ @Override
public void run() {
try {
DataInputStream in = new DataInputStream(socket.getInputStream());