mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-12 02:05:50 +00:00
HSQLDB issue, resource leak prevention, v1 differences
HSQLDB v2.4.0 had some issue with non-padded, case-insensitive string comparisons. This is fixed in svn r5836-ish of HSQLDB but yet to be pushed out to new HSQLDB release. So this commit includes hsqldb-r5836.jar and modified pom.xml/.classpath for now. No need for duplicate, hidden creatorPublicKey in CancelOrderTransactionData, CreateOrderTransactionData and CreatePollTransactionData. Various changes to use more try-with-resources, especially with JDBC objects like Connection, Statement, PreparedStatement, ResultSet. Added loads of missing @Override annotations. Fixed bug in Asset exchange order matching where the matching logic loop would incorrectly adjust temporary amount fulfilled by the "want" asset amount (in matchedAmount) instead of the "have" asset amount (in tradePrice). Disabled check for duplicate asset name in IssueAssetTransactions for old v1 transactions. In HSQLDB repository we now use ResultSet.getTimestamp(index, UTC-calendar) to make sure we only store/fetch UTC timestamps. The UTC-calendar is made using static final TimeZone called HSQLDBRepository.UTC. To keep asset IDs in line with v1, Assets.asset_id values are generated on-the-fly in HSQLDB using a "before insert" trigger on Assets table. Corresponding code calling HSQLDBRepository.callIdentity() replaced with SELECT statement instead. Moved most of the HSQLDB connection properties from the connection URL to explicit code in HSQLDBRepositoryFactory. Fixed incorrect 'amount' lengths in PaymentTransformer, as used by MultiPayment and Arbitrary transaction types. Added support for mangled arbitrary transaction bytes when generating/verifying a v1 transaction signature. In v1 Arbitrary transactions, bytes-for-signing are lost prior to final payment (but only if there are any payments). Added corresponding code for multi-payment transactions in the same vein.
This commit is contained in:
parent
7da84b2b85
commit
e56d8f5e02
@ -17,5 +17,6 @@
|
|||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
|
<classpathentry kind="lib" path="lib/hsqldb-r5836.jar"/>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
BIN
lib/hsqldb-r5836.jar
Normal file
BIN
lib/hsqldb-r5836.jar
Normal file
Binary file not shown.
5
pom.xml
5
pom.xml
@ -17,11 +17,6 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.hsqldb</groupId>
|
|
||||||
<artifactId>hsqldb</artifactId>
|
|
||||||
<version>2.4.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.googlecode.json-simple</groupId>
|
<groupId>com.googlecode.json-simple</groupId>
|
||||||
<artifactId>json-simple</artifactId>
|
<artifactId>json-simple</artifactId>
|
||||||
|
@ -7,7 +7,6 @@ import qora.transaction.Transaction;
|
|||||||
public class CancelOrderTransactionData extends TransactionData {
|
public class CancelOrderTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private byte[] creatorPublicKey;
|
|
||||||
private byte[] orderId;
|
private byte[] orderId;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
@ -15,7 +14,6 @@ public class CancelOrderTransactionData extends TransactionData {
|
|||||||
public CancelOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
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);
|
super(Transaction.TransactionType.CANCEL_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
this.creatorPublicKey = creatorPublicKey;
|
|
||||||
this.orderId = orderId;
|
this.orderId = orderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +23,6 @@ public class CancelOrderTransactionData extends TransactionData {
|
|||||||
|
|
||||||
// Getters/Setters
|
// Getters/Setters
|
||||||
|
|
||||||
public byte[] getCreatorPublicKey() {
|
|
||||||
return this.creatorPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getOrderId() {
|
public byte[] getOrderId() {
|
||||||
return this.orderId;
|
return this.orderId;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import qora.transaction.Transaction.TransactionType;
|
|||||||
public class CreateOrderTransactionData extends TransactionData {
|
public class CreateOrderTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private byte[] creatorPublicKey;
|
|
||||||
private long haveAssetId;
|
private long haveAssetId;
|
||||||
private long wantAssetId;
|
private long wantAssetId;
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
@ -19,7 +18,6 @@ public class CreateOrderTransactionData extends TransactionData {
|
|||||||
long timestamp, byte[] reference, byte[] signature) {
|
long timestamp, byte[] reference, byte[] signature) {
|
||||||
super(TransactionType.CREATE_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
|
super(TransactionType.CREATE_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
this.creatorPublicKey = creatorPublicKey;
|
|
||||||
this.haveAssetId = haveAssetId;
|
this.haveAssetId = haveAssetId;
|
||||||
this.wantAssetId = wantAssetId;
|
this.wantAssetId = wantAssetId;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
@ -33,10 +31,6 @@ public class CreateOrderTransactionData extends TransactionData {
|
|||||||
|
|
||||||
// Getters/Setters
|
// Getters/Setters
|
||||||
|
|
||||||
public byte[] getCreatorPublicKey() {
|
|
||||||
return this.creatorPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getHaveAssetId() {
|
public long getHaveAssetId() {
|
||||||
return this.haveAssetId;
|
return this.haveAssetId;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import qora.transaction.Transaction;
|
|||||||
public class CreatePollTransactionData extends TransactionData {
|
public class CreatePollTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private byte[] creatorPublicKey;
|
|
||||||
private String owner;
|
private String owner;
|
||||||
private String pollName;
|
private String pollName;
|
||||||
private String description;
|
private String description;
|
||||||
@ -21,7 +20,6 @@ public class CreatePollTransactionData extends TransactionData {
|
|||||||
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||||
super(Transaction.TransactionType.CREATE_POLL, fee, creatorPublicKey, timestamp, reference, signature);
|
super(Transaction.TransactionType.CREATE_POLL, fee, creatorPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
this.creatorPublicKey = creatorPublicKey;
|
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.pollName = pollName;
|
this.pollName = pollName;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@ -35,10 +33,6 @@ public class CreatePollTransactionData extends TransactionData {
|
|||||||
|
|
||||||
// Getters/setters
|
// Getters/setters
|
||||||
|
|
||||||
public byte[] getCreatorPublicKey() {
|
|
||||||
return this.creatorPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOwner() {
|
public String getOwner() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
@ -44,21 +44,14 @@ public class migrate {
|
|||||||
private static Map<String, byte[]> publicKeyByAddress = new HashMap<String, byte[]>();
|
private static Map<String, byte[]> publicKeyByAddress = new HashMap<String, byte[]>();
|
||||||
|
|
||||||
public static Object fetchBlockJSON(int height) throws IOException {
|
public static Object fetchBlockJSON(int height) throws IOException {
|
||||||
InputStream is;
|
try (InputStream is = new URL("http://localhost:9085/blocks/byheight/" + height).openStream();
|
||||||
|
InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"));
|
||||||
try {
|
BufferedReader reader = new BufferedReader(isr)) {
|
||||||
is = new URL("http://localhost:9085/blocks/byheight/" + height).openStream();
|
return JSONValue.parseWithException(reader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
|
|
||||||
return JSONValue.parseWithException(reader);
|
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
|
||||||
is.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +62,8 @@ public class migrate {
|
|||||||
|
|
||||||
InputStream is = new URL("http://localhost:9085/addresses/publickey/" + address).openStream();
|
InputStream is = new URL("http://localhost:9085/addresses/publickey/" + address).openStream();
|
||||||
|
|
||||||
try {
|
try (InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"))) {
|
||||||
String publicKey58 = CharStreams.toString(new InputStreamReader(is, Charset.forName("UTF-8")));
|
String publicKey58 = CharStreams.toString(isr);
|
||||||
|
|
||||||
byte[] publicKey = Base58.decode(publicKey58);
|
byte[] publicKey = Base58.decode(publicKey58);
|
||||||
publicKeyByAddress.put(address, publicKey);
|
publicKeyByAddress.put(address, publicKey);
|
||||||
@ -81,12 +74,12 @@ public class migrate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void savePublicKeys(Connection connection) throws SQLException {
|
public static void savePublicKeys(Connection connection) throws SQLException {
|
||||||
PreparedStatement pStmt = connection.prepareStatement("INSERT IGNORE INTO Test_public_keys VALUES (?, ?)");
|
try (PreparedStatement pStmt = connection.prepareStatement("INSERT IGNORE INTO Test_public_keys VALUES (?, ?)")) {
|
||||||
|
for (Entry<String, byte[]> entry : publicKeyByAddress.entrySet()) {
|
||||||
for (Entry<String, byte[]> entry : publicKeyByAddress.entrySet()) {
|
pStmt.setString(1, entry.getKey());
|
||||||
pStmt.setString(1, entry.getKey());
|
pStmt.setBytes(2, entry.getValue());
|
||||||
pStmt.setBytes(2, entry.getValue());
|
pStmt.execute();
|
||||||
pStmt.execute();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +96,7 @@ public class migrate {
|
|||||||
return output.toString();
|
return output.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
public static void main(String args[]) throws SQLException, DataException, IOException {
|
public static void main(String args[]) throws SQLException, DataException, IOException {
|
||||||
// Genesis public key
|
// Genesis public key
|
||||||
publicKeyByAddress.put(GENESIS_ADDRESS, GENESIS_PUBLICKEY);
|
publicKeyByAddress.put(GENESIS_ADDRESS, GENESIS_PUBLICKEY);
|
||||||
@ -132,14 +126,14 @@ public class migrate {
|
|||||||
.prepareStatement("INSERT INTO PaymentTransactions " + formatWithPlaceholders("signature", "sender", "recipient", "amount"));
|
.prepareStatement("INSERT INTO PaymentTransactions " + formatWithPlaceholders("signature", "sender", "recipient", "amount"));
|
||||||
PreparedStatement registerNamePStmt = c
|
PreparedStatement registerNamePStmt = c
|
||||||
.prepareStatement("INSERT INTO RegisterNameTransactions " + formatWithPlaceholders("signature", "registrant", "name", "owner", "data"));
|
.prepareStatement("INSERT INTO RegisterNameTransactions " + formatWithPlaceholders("signature", "registrant", "name", "owner", "data"));
|
||||||
PreparedStatement updateNamePStmt = c
|
PreparedStatement updateNamePStmt = c.prepareStatement(
|
||||||
.prepareStatement("INSERT INTO UpdateNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "new_owner", "new_data", "name_reference"));
|
"INSERT INTO UpdateNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "new_owner", "new_data", "name_reference"));
|
||||||
PreparedStatement sellNamePStmt = c
|
PreparedStatement sellNamePStmt = c
|
||||||
.prepareStatement("INSERT INTO SellNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "amount"));
|
.prepareStatement("INSERT INTO SellNameTransactions " + formatWithPlaceholders("signature", "owner", "name", "amount"));
|
||||||
PreparedStatement cancelSellNamePStmt = c
|
PreparedStatement cancelSellNamePStmt = c
|
||||||
.prepareStatement("INSERT INTO CancelSellNameTransactions " + formatWithPlaceholders("signature", "owner", "name"));
|
.prepareStatement("INSERT INTO CancelSellNameTransactions " + formatWithPlaceholders("signature", "owner", "name"));
|
||||||
PreparedStatement buyNamePStmt = c
|
PreparedStatement buyNamePStmt = c.prepareStatement(
|
||||||
.prepareStatement("INSERT INTO BuyNameTransactions " + formatWithPlaceholders("signature", "buyer", "name", "seller", "amount", "name_reference"));
|
"INSERT INTO BuyNameTransactions " + formatWithPlaceholders("signature", "buyer", "name", "seller", "amount", "name_reference"));
|
||||||
PreparedStatement createPollPStmt = c
|
PreparedStatement createPollPStmt = c
|
||||||
.prepareStatement("INSERT INTO CreatePollTransactions " + formatWithPlaceholders("signature", "creator", "owner", "poll_name", "description"));
|
.prepareStatement("INSERT INTO CreatePollTransactions " + formatWithPlaceholders("signature", "creator", "owner", "poll_name", "description"));
|
||||||
PreparedStatement createPollOptionPStmt = c
|
PreparedStatement createPollOptionPStmt = c
|
||||||
|
@ -163,7 +163,7 @@ public class Order {
|
|||||||
|
|
||||||
// Trade can go ahead!
|
// 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);
|
BigDecimal tradePrice = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8);
|
||||||
|
|
||||||
// Construct trade
|
// Construct trade
|
||||||
@ -174,7 +174,7 @@ public class Order {
|
|||||||
trade.process();
|
trade.process();
|
||||||
|
|
||||||
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
|
// 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
|
// Continue on to process other open orders in case we still have amount left to match
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,11 @@ public class BlockChain {
|
|||||||
private static final long ASSETS_RELEASE_TIMESTAMP = 0L; // From Qora epoch
|
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 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 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 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 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 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.
|
* Some sort start-up/initialization/checking method.
|
||||||
@ -164,4 +166,11 @@ public class BlockChain {
|
|||||||
return CREATE_ORDER_V2_TIMESTAMP;
|
return CREATE_ORDER_V2_TIMESTAMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getArbitraryTransactionV2Timestamp() {
|
||||||
|
if (Settings.getInstance().isTestNet())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ARBITRARY_TRANSACTION_V2_TIMESTAMP;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
List<Account> recipients = new ArrayList<Account>();
|
List<Account> recipients = new ArrayList<Account>();
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -32,14 +32,17 @@ public class CancelOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<Account> getRecipientAccounts() {
|
||||||
return new ArrayList<Account>();
|
return new ArrayList<Account>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
return account.getAddress().equals(this.getCreator().getAddress());
|
return account.getAddress().equals(this.getCreator().getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
@ -51,6 +54,7 @@ public class CancelOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
|
@Override
|
||||||
public Account getCreator() throws DataException {
|
public Account getCreator() throws DataException {
|
||||||
return new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey());
|
return new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey());
|
||||||
}
|
}
|
||||||
|
@ -33,14 +33,17 @@ public class CreateOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() {
|
public List<Account> getRecipientAccounts() {
|
||||||
return new ArrayList<Account>();
|
return new ArrayList<Account>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
return account.getAddress().equals(this.getCreator().getAddress());
|
return account.getAddress().equals(this.getCreator().getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
@ -52,6 +55,7 @@ public class CreateOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
|
@Override
|
||||||
public Account getCreator() throws DataException {
|
public Account getCreator() throws DataException {
|
||||||
return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey());
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,12 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -51,6 +53,7 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
@ -63,6 +66,7 @@ public class CreatePollTransaction extends Transaction {
|
|||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
|
@Override
|
||||||
public Account getCreator() throws DataException {
|
public Account getCreator() throws DataException {
|
||||||
return new PublicKeyAccount(this.repository, this.createPollTransactionData.getCreatorPublicKey());
|
return new PublicKeyAccount(this.repository, this.createPollTransactionData.getCreatorPublicKey());
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,12 @@ public class GenesisTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, genesisTransactionData.getRecipient()));
|
return Collections.singletonList(new Account(this.repository, genesisTransactionData.getRecipient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ public class GenesisTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -36,10 +36,12 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(getOwner());
|
return Collections.singletonList(getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
@ -117,8 +120,9 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
// XXX: Surely we want to check the asset name isn't already taken? This check is not present in gen1.
|
// 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()))
|
if (issueAssetTransactionData.getTimestamp() >= BlockChain.getIssueAssetV2Timestamp())
|
||||||
return ValidationResult.ASSET_ALREADY_EXISTS;
|
if (this.repository.getAssetRepository().assetExists(issueAssetTransactionData.getAssetName()))
|
||||||
|
return ValidationResult.ASSET_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,12 @@ public class MessageTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient()));
|
return Collections.singletonList(new Account(this.repository, messageTransactionData.getRecipient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ public class MessageTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -34,6 +34,7 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
List<Account> recipients = new ArrayList<Account>();
|
List<Account> recipients = new ArrayList<Account>();
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -56,6 +58,7 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -30,10 +30,12 @@ public class PaymentTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, paymentTransactionData.getRecipient()));
|
return Collections.singletonList(new Account(this.repository, paymentTransactionData.getRecipient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ public class PaymentTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -31,10 +31,12 @@ public class TransferAssetTransaction extends Transaction {
|
|||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<Account> getRecipientAccounts() throws DataException {
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
return Collections.singletonList(new Account(this.repository, transferAssetTransactionData.getRecipient()));
|
return Collections.singletonList(new Account(this.repository, transferAssetTransactionData.getRecipient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInvolved(Account account) throws DataException {
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ public class TransferAssetTransaction extends Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BigDecimal getAmount(Account account) throws DataException {
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
String address = account.getAddress();
|
String address = account.getAddress();
|
||||||
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
@ -18,6 +18,7 @@ public interface Repository extends AutoCloseable {
|
|||||||
|
|
||||||
public void discardChanges() throws DataException;
|
public void discardChanges() throws DataException;
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close() throws DataException;
|
public void close() throws DataException;
|
||||||
|
|
||||||
public void rebuild() throws DataException;
|
public void rebuild() throws DataException;
|
||||||
|
@ -17,9 +17,9 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AccountData getAccount(String address) throws DataException {
|
public AccountData getAccount(String address) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address)) {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", address);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(AccountData accountData) throws DataException {
|
public void save(AccountData accountData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
|
||||||
saveHelper.bind("account", accountData.getAddress()).bind("reference", accountData.getReference());
|
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 {
|
public AccountBalanceData getBalance(String address, long assetId) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId)) {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(AccountBalanceData accountBalanceData) throws DataException {
|
public void save(AccountBalanceData accountBalanceData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
|
||||||
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance",
|
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 {
|
public void delete(String address, long assetId) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("AccountBalances", "account = ? and asset_id = ?", address, assetId);
|
this.repository.delete("AccountBalances", "account = ? and asset_id = ?", address, assetId);
|
||||||
|
@ -5,6 +5,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import data.assets.AssetData;
|
import data.assets.AssetData;
|
||||||
@ -23,10 +24,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
// Assets
|
// Assets
|
||||||
|
|
||||||
|
@Override
|
||||||
public AssetData fromAssetId(long assetId) throws DataException {
|
public AssetData fromAssetId(long assetId) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet resultSet = this.repository
|
.checkedExecute("SELECT owner, asset_name, description, quantity, is_divisible, reference FROM Assets WHERE asset_id = ?", assetId)) {
|
||||||
.checkedExecute("SELECT owner, asset_name, description, quantity, is_divisible, reference FROM Assets WHERE asset_id = ?", assetId);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -43,10 +44,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AssetData fromAssetName(String assetName) throws DataException {
|
public AssetData fromAssetName(String assetName) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet resultSet = this.repository
|
.checkedExecute("SELECT owner, asset_id, description, quantity, is_divisible, reference FROM Assets WHERE asset_name = ?", assetName)) {
|
||||||
.checkedExecute("SELECT owner, asset_id, description, quantity, is_divisible, reference FROM Assets WHERE asset_name = ?", assetName);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean assetExists(long assetId) throws DataException {
|
public boolean assetExists(long assetId) throws DataException {
|
||||||
try {
|
try {
|
||||||
return this.repository.exists("Assets", "asset_id = ?", assetId);
|
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 {
|
public boolean assetExists(String assetName) throws DataException {
|
||||||
try {
|
try {
|
||||||
return this.repository.exists("Assets", "asset_name = ?", assetName);
|
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 {
|
public void save(AssetData assetData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Assets");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Assets");
|
||||||
|
|
||||||
@ -89,13 +93,21 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
try {
|
try {
|
||||||
saveHelper.execute(this.repository);
|
saveHelper.execute(this.repository);
|
||||||
|
|
||||||
if (assetData.getAssetId() == null)
|
if (assetData.getAssetId() == null) {
|
||||||
assetData.setAssetId(this.repository.callIdentity());
|
// 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) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to save asset into repository", e);
|
throw new DataException("Unable to save asset into repository", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(long assetId) throws DataException {
|
public void delete(long assetId) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("Assets", "asset_id = ?", assetId);
|
this.repository.delete("Assets", "asset_id = ?", assetId);
|
||||||
@ -106,11 +118,11 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
// Orders
|
// Orders
|
||||||
|
|
||||||
|
@Override
|
||||||
public OrderData fromOrderId(byte[] orderId) throws DataException {
|
public OrderData fromOrderId(byte[] orderId) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
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 = ?",
|
||||||
"SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled FROM AssetOrders WHERE asset_order_id = ?",
|
orderId)) {
|
||||||
orderId);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -120,7 +132,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
BigDecimal amount = resultSet.getBigDecimal(4);
|
BigDecimal amount = resultSet.getBigDecimal(4);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(5);
|
BigDecimal fulfilled = resultSet.getBigDecimal(5);
|
||||||
BigDecimal price = resultSet.getBigDecimal(6);
|
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 isClosed = resultSet.getBoolean(8);
|
||||||
boolean isFulfilled = resultSet.getBoolean(9);
|
boolean isFulfilled = resultSet.getBoolean(9);
|
||||||
|
|
||||||
@ -130,14 +142,14 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<OrderData> getOpenOrders(long haveAssetId, long wantAssetId) throws DataException {
|
public List<OrderData> getOpenOrders(long haveAssetId, long wantAssetId) throws DataException {
|
||||||
List<OrderData> orders = new ArrayList<OrderData>();
|
List<OrderData> orders = new ArrayList<OrderData>();
|
||||||
|
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
ResultSet resultSet = this.repository.checkedExecute(
|
"SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders "
|
||||||
"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",
|
||||||
+ "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE ORDER BY price ASC",
|
haveAssetId, wantAssetId)) {
|
||||||
haveAssetId, wantAssetId);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return orders;
|
return orders;
|
||||||
|
|
||||||
@ -147,7 +159,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
BigDecimal amount = resultSet.getBigDecimal(3);
|
BigDecimal amount = resultSet.getBigDecimal(3);
|
||||||
BigDecimal fulfilled = resultSet.getBigDecimal(4);
|
BigDecimal fulfilled = resultSet.getBigDecimal(4);
|
||||||
BigDecimal price = resultSet.getBigDecimal(5);
|
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 isClosed = false;
|
||||||
boolean isFulfilled = false;
|
boolean isFulfilled = false;
|
||||||
|
|
||||||
@ -162,6 +174,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(OrderData orderData) throws DataException {
|
public void save(OrderData orderData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetOrders");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetOrders");
|
||||||
|
|
||||||
@ -177,6 +190,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(byte[] orderId) throws DataException {
|
public void delete(byte[] orderId) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("AssetOrders", "asset_order_id = ?", orderId);
|
this.repository.delete("AssetOrders", "asset_order_id = ?", orderId);
|
||||||
@ -187,12 +201,12 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
// Trades
|
// Trades
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<TradeData> getOrdersTrades(byte[] initiatingOrderId) throws DataException {
|
public List<TradeData> getOrdersTrades(byte[] initiatingOrderId) throws DataException {
|
||||||
List<TradeData> trades = new ArrayList<TradeData>();
|
List<TradeData> trades = new ArrayList<TradeData>();
|
||||||
|
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT target_order_id, amount, price, traded FROM AssetTrades WHERE initiating_order_id = ?",
|
.checkedExecute("SELECT target_order_id, amount, price, traded FROM AssetTrades WHERE initiating_order_id = ?", initiatingOrderId)) {
|
||||||
initiatingOrderId);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return trades;
|
return trades;
|
||||||
|
|
||||||
@ -200,7 +214,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
byte[] targetOrderId = resultSet.getBytes(1);
|
byte[] targetOrderId = resultSet.getBytes(1);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(2);
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
BigDecimal price = resultSet.getBigDecimal(3);
|
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);
|
TradeData trade = new TradeData(initiatingOrderId, targetOrderId, amount, price, timestamp);
|
||||||
trades.add(trade);
|
trades.add(trade);
|
||||||
@ -212,6 +226,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(TradeData tradeData) throws DataException {
|
public void save(TradeData tradeData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetTrades");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AssetTrades");
|
||||||
|
|
||||||
@ -225,6 +240,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(TradeData tradeData) throws DataException {
|
public void delete(TradeData tradeData) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("AssetTrades", "initiating_order_id = ? AND target_order_id = ? AND amount = ? AND price = ?", tradeData.getInitiator(),
|
this.repository.delete("AssetTrades", "initiating_order_id = ? AND target_order_id = ? AND amount = ? AND price = ?", tradeData.getInitiator(),
|
||||||
|
@ -5,6 +5,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
@ -25,23 +26,23 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockData getBlockFromResultSet(ResultSet rs) throws DataException {
|
private BlockData getBlockFromResultSet(ResultSet resultSet) throws DataException {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int version = rs.getInt(1);
|
int version = resultSet.getInt(1);
|
||||||
byte[] reference = rs.getBytes(2);
|
byte[] reference = resultSet.getBytes(2);
|
||||||
int transactionCount = rs.getInt(3);
|
int transactionCount = resultSet.getInt(3);
|
||||||
BigDecimal totalFees = rs.getBigDecimal(4);
|
BigDecimal totalFees = resultSet.getBigDecimal(4);
|
||||||
byte[] transactionsSignature = rs.getBytes(5);
|
byte[] transactionsSignature = resultSet.getBytes(5);
|
||||||
int height = rs.getInt(6);
|
int height = resultSet.getInt(6);
|
||||||
long timestamp = rs.getTimestamp(7).getTime();
|
long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
BigDecimal generatingBalance = rs.getBigDecimal(8);
|
BigDecimal generatingBalance = resultSet.getBigDecimal(8);
|
||||||
byte[] generatorPublicKey = rs.getBytes(9);
|
byte[] generatorPublicKey = resultSet.getBytes(9);
|
||||||
byte[] generatorSignature = rs.getBytes(10);
|
byte[] generatorSignature = resultSet.getBytes(10);
|
||||||
byte[] atBytes = rs.getBytes(11);
|
byte[] atBytes = resultSet.getBytes(11);
|
||||||
BigDecimal atFees = rs.getBigDecimal(12);
|
BigDecimal atFees = resultSet.getBigDecimal(12);
|
||||||
|
|
||||||
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
|
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
|
||||||
generatorPublicKey, generatorSignature, atBytes, atFees);
|
generatorPublicKey, generatorSignature, atBytes, atFees);
|
||||||
@ -50,76 +51,77 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BlockData fromSignature(byte[] signature) throws DataException {
|
public BlockData fromSignature(byte[] signature) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
|
return getBlockFromResultSet(resultSet);
|
||||||
return getBlockFromResultSet(rs);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error loading data from DB", e);
|
throw new DataException("Error loading data from DB", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BlockData fromReference(byte[] reference) throws DataException {
|
public BlockData fromReference(byte[] reference) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", reference)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", reference);
|
return getBlockFromResultSet(resultSet);
|
||||||
return getBlockFromResultSet(rs);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error loading data from DB", e);
|
throw new DataException("Error loading data from DB", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BlockData fromHeight(int height) throws DataException {
|
public BlockData fromHeight(int height) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height);
|
return getBlockFromResultSet(resultSet);
|
||||||
return getBlockFromResultSet(rs);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error loading data from DB", e);
|
throw new DataException("Error loading data from DB", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getHeightFromSignature(byte[] signature) throws DataException {
|
public int getHeightFromSignature(byte[] signature) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return rs.getInt(1);
|
return resultSet.getInt(1);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error obtaining block height from repository", e);
|
throw new DataException("Error obtaining block height from repository", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getBlockchainHeight() throws DataException {
|
public int getBlockchainHeight() throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT MAX(height) FROM Blocks")) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT MAX(height) FROM Blocks");
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return rs.getInt(1);
|
return resultSet.getInt(1);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error obtaining blockchain height from repository", e);
|
throw new DataException("Error obtaining blockchain height from repository", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BlockData getLastBlock() throws DataException {
|
public BlockData getLastBlock() throws DataException {
|
||||||
return fromHeight(getBlockchainHeight());
|
return fromHeight(getBlockchainHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<TransactionData> getTransactionsFromSignature(byte[] signature) throws DataException {
|
public List<TransactionData> getTransactionsFromSignature(byte[] signature) throws DataException {
|
||||||
List<TransactionData> transactions = new ArrayList<TransactionData>();
|
List<TransactionData> transactions = new ArrayList<TransactionData>();
|
||||||
|
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return transactions; // No transactions in this block
|
return transactions; // No transactions in this block
|
||||||
|
|
||||||
TransactionRepository transactionRepo = this.repository.getTransactionRepository();
|
TransactionRepository transactionRepo = this.repository.getTransactionRepository();
|
||||||
|
|
||||||
// NB: do-while loop because .checkedExecute() implicitly calls ResultSet.next() for us
|
// NB: do-while loop because .checkedExecute() implicitly calls ResultSet.next() for us
|
||||||
do {
|
do {
|
||||||
byte[] transactionSignature = rs.getBytes(1);
|
byte[] transactionSignature = resultSet.getBytes(1);
|
||||||
transactions.add(transactionRepo.fromSignature(transactionSignature));
|
transactions.add(transactionRepo.fromSignature(transactionSignature));
|
||||||
} while (rs.next());
|
} while (resultSet.next());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException(e);
|
throw new DataException(e);
|
||||||
}
|
}
|
||||||
@ -127,6 +129,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
return transactions;
|
return transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(BlockData blockData) throws DataException {
|
public void save(BlockData blockData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks");
|
||||||
|
|
||||||
@ -144,6 +147,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(BlockData blockData) throws DataException {
|
public void delete(BlockData blockData) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("Blocks", "signature = ?", blockData.getSignature());
|
this.repository.delete("Blocks", "signature = ?", blockData.getSignature());
|
||||||
@ -152,6 +156,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(BlockTransactionData blockTransactionData) throws DataException {
|
public void save(BlockTransactionData blockTransactionData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("BlockTransactions");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("BlockTransactions");
|
||||||
saveHelper.bind("block_signature", blockTransactionData.getBlockSignature()).bind("sequence", blockTransactionData.getSequence())
|
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 {
|
public void delete(BlockTransactionData blockTransactionData) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("BlockTransactions", "block_signature = ? AND sequence = ? AND transaction_signature = ?",
|
this.repository.delete("BlockTransactions", "block_signature = ? AND sequence = ? AND transaction_signature = ?",
|
||||||
|
@ -36,14 +36,12 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
private static int fetchDatabaseVersion(Connection connection) throws SQLException {
|
private static int fetchDatabaseVersion(Connection connection) throws SQLException {
|
||||||
int databaseVersion = 0;
|
int databaseVersion = 0;
|
||||||
|
|
||||||
try {
|
try (Statement stmt = connection.createStatement()) {
|
||||||
Statement stmt = connection.createStatement();
|
if (stmt.execute("SELECT version FROM DatabaseInfo"))
|
||||||
if (stmt.execute("SELECT version FROM DatabaseInfo")) {
|
try (ResultSet resultSet = stmt.getResultSet()) {
|
||||||
ResultSet rs = stmt.getResultSet();
|
if (resultSet.next())
|
||||||
|
databaseVersion = resultSet.getInt(1);
|
||||||
if (rs.next())
|
}
|
||||||
databaseVersion = rs.getInt(1);
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
// empty database
|
// empty database
|
||||||
}
|
}
|
||||||
@ -60,292 +58,298 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
private static boolean databaseUpdating(Connection connection) throws SQLException {
|
private static boolean databaseUpdating(Connection connection) throws SQLException {
|
||||||
int databaseVersion = fetchDatabaseVersion(connection);
|
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
|
* Try not to add too many constraints as much of these checks will be performed during transaction validation. Also some constraints might be too
|
||||||
* on competing unconfirmed transactions.
|
* 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
|
* Only really add "ON DELETE CASCADE" to sub-tables that store type-specific data. For example on sub-types of Transactions like
|
||||||
* counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to Transactions' "signature". We
|
* PaymentTransactions. A counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to
|
||||||
* want to database to automatically delete complete transaction data (Transactions row and corresponding PaymentTransactions row), but leave deleting
|
* Transactions' "signature". We want to database to automatically delete complete transaction data (Transactions row and corresponding
|
||||||
* less related table rows (Assets) to the Java logic.
|
* PaymentTransactions row), but leave deleting less related table rows (Assets) to the Java logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
switch (databaseVersion) {
|
switch (databaseVersion) {
|
||||||
case 0:
|
case 0:
|
||||||
// create from new
|
// create from new
|
||||||
stmt.execute("SET DATABASE DEFAULT TABLE TYPE CACHED");
|
stmt.execute("SET DATABASE DEFAULT TABLE TYPE CACHED");
|
||||||
stmt.execute("SET DATABASE COLLATION SQL_TEXT NO PAD");
|
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_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("CREATE COLLATION SQL_TEXT_NO_PAD FOR SQL_TEXT FROM SQL_TEXT NO PAD");
|
||||||
stmt.execute("SET FILES SPACE TRUE");
|
stmt.execute("SET FILES SPACE TRUE");
|
||||||
stmt.execute("CREATE TABLE DatabaseInfo ( version INTEGER NOT NULL )");
|
stmt.execute("CREATE TABLE DatabaseInfo ( version INTEGER NOT NULL )");
|
||||||
stmt.execute("INSERT INTO DatabaseInfo VALUES ( 0 )");
|
stmt.execute("INSERT INTO DatabaseInfo VALUES ( 0 )");
|
||||||
stmt.execute("CREATE TYPE BlockSignature AS VARBINARY(128)");
|
stmt.execute("CREATE TYPE BlockSignature AS VARBINARY(128)");
|
||||||
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
||||||
stmt.execute("CREATE TYPE QoraAddress AS VARCHAR(36)");
|
stmt.execute("CREATE TYPE QoraAddress AS VARCHAR(36)");
|
||||||
stmt.execute("CREATE TYPE QoraPublicKey AS VARBINARY(32)");
|
stmt.execute("CREATE TYPE QoraPublicKey AS VARBINARY(32)");
|
||||||
stmt.execute("CREATE TYPE QoraAmount AS DECIMAL(19, 8)");
|
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 RegisteredName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
|
||||||
stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
|
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 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 PollOption AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD");
|
||||||
stmt.execute("CREATE TYPE PollOptionIndex AS INTEGER");
|
stmt.execute("CREATE TYPE PollOptionIndex AS INTEGER");
|
||||||
stmt.execute("CREATE TYPE DataHash AS VARBINARY(32)");
|
stmt.execute("CREATE TYPE DataHash AS VARBINARY(32)");
|
||||||
stmt.execute("CREATE TYPE AssetID AS BIGINT");
|
stmt.execute("CREATE TYPE AssetID AS BIGINT");
|
||||||
stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_NO_PAD");
|
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 AssetOrderID AS VARBINARY(64)");
|
||||||
stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
|
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");
|
stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC_NO_PAD");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// Blocks
|
// Blocks
|
||||||
stmt.execute("CREATE TABLE Blocks (signature BlockSignature PRIMARY KEY, version TINYINT NOT NULL, reference BlockSignature, "
|
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, "
|
+ "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, "
|
+ "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)");
|
+ "generator QoraPublicKey NOT NULL, generator_signature Signature NOT NULL, AT_data VARBINARY(20000), AT_fees QoraAmount)");
|
||||||
// For finding blocks by height.
|
// For finding blocks by height.
|
||||||
stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
|
stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
|
||||||
// For finding blocks by the account that generated them.
|
// For finding blocks by the account that generated them.
|
||||||
stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
|
stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
|
||||||
// For finding blocks by reference, e.g. child blocks.
|
// For finding blocks by reference, e.g. child blocks.
|
||||||
stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
|
stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
|
||||||
// Use a separate table space as this table will be very large.
|
// Use a separate table space as this table will be very large.
|
||||||
stmt.execute("SET TABLE Blocks NEW SPACE");
|
stmt.execute("SET TABLE Blocks NEW SPACE");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
// Generic transactions (null reference, creator and milestone_block for genesis transactions)
|
// 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, "
|
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)");
|
+ "creator QoraPublicKey, creation TIMESTAMP WITH TIME ZONE NOT NULL, fee QoraAmount NOT NULL, milestone_block BlockSignature)");
|
||||||
// For finding transactions by transaction type.
|
// For finding transactions by transaction type.
|
||||||
stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
|
stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
|
||||||
// For finding transactions using timestamp.
|
// For finding transactions using timestamp.
|
||||||
stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
|
stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
|
||||||
// For when a user wants to lookup ALL transactions they have created, regardless of type.
|
// For when a user wants to lookup ALL transactions they have created, regardless of type.
|
||||||
stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
|
stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
|
||||||
// For finding transactions by reference, e.g. child transactions.
|
// For finding transactions by reference, e.g. child transactions.
|
||||||
stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
|
stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
|
||||||
// Use a separate table space as this table will be very large.
|
// Use a separate table space as this table will be very large.
|
||||||
stmt.execute("SET TABLE Transactions NEW SPACE");
|
stmt.execute("SET TABLE Transactions NEW SPACE");
|
||||||
|
|
||||||
// Transaction-Block mapping ("signature" is unique as a transaction cannot be included in more than one block)
|
// 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, "
|
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, "
|
+ "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)");
|
+ "FOREIGN KEY (block_signature) REFERENCES Blocks (signature) ON DELETE CASCADE)");
|
||||||
// Use a separate table space as this table will be very large.
|
// Use a separate table space as this table will be very large.
|
||||||
stmt.execute("SET TABLE BlockTransactions NEW SPACE");
|
stmt.execute("SET TABLE BlockTransactions NEW SPACE");
|
||||||
|
|
||||||
// Unconfirmed transactions
|
// Unconfirmed transactions
|
||||||
// XXX Do we need this? If a transaction doesn't have a corresponding BlockTransactions record then it's unconfirmed?
|
// 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 TABLE UnconfirmedTransactions (signature Signature PRIMARY KEY, expiry TIMESTAMP WITH TIME ZONE NOT NULL)");
|
||||||
stmt.execute("CREATE INDEX UnconfirmedTransactionExpiryIndex ON UnconfirmedTransactions (expiry)");
|
stmt.execute("CREATE INDEX UnconfirmedTransactionExpiryIndex ON UnconfirmedTransactions (expiry)");
|
||||||
|
|
||||||
// Transaction recipients
|
// Transaction recipients
|
||||||
stmt.execute("CREATE TABLE TransactionRecipients (signature Signature, recipient QoraAddress NOT NULL, "
|
stmt.execute("CREATE TABLE TransactionRecipients (signature Signature, recipient QoraAddress NOT NULL, "
|
||||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
// Use a separate table space as this table will be very large.
|
// Use a separate table space as this table will be very large.
|
||||||
stmt.execute("SET TABLE TransactionRecipients NEW SPACE");
|
stmt.execute("SET TABLE TransactionRecipients NEW SPACE");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// Genesis Transactions
|
// Genesis Transactions
|
||||||
stmt.execute("CREATE TABLE GenesisTransactions (signature Signature, recipient QoraAddress NOT NULL, "
|
stmt.execute("CREATE TABLE GenesisTransactions (signature Signature, recipient QoraAddress NOT NULL, "
|
||||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
// Payment Transactions
|
// Payment Transactions
|
||||||
stmt.execute("CREATE TABLE PaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
stmt.execute("CREATE TABLE PaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
// Register Name Transactions
|
// Register Name Transactions
|
||||||
stmt.execute("CREATE TABLE RegisterNameTransactions (signature Signature, registrant QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
stmt.execute("CREATE TABLE RegisterNameTransactions (signature Signature, registrant QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||||
+ "owner QoraAddress NOT NULL, data NameData NOT NULL, "
|
+ "owner QoraAddress NOT NULL, data NameData NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
// Update Name Transactions
|
// Update Name Transactions
|
||||||
stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
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, "
|
+ "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)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
// Sell Name Transactions
|
// Sell Name Transactions
|
||||||
stmt.execute("CREATE TABLE SellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
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)");
|
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8:
|
case 8:
|
||||||
// Cancel Sell Name Transactions
|
// Cancel Sell Name Transactions
|
||||||
stmt.execute("CREATE TABLE CancelSellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
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)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
// Buy Name Transactions
|
// Buy Name Transactions
|
||||||
stmt.execute("CREATE TABLE BuyNameTransactions (signature Signature, buyer QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
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, "
|
+ "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)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 10:
|
case 10:
|
||||||
// Create Poll Transactions
|
// Create Poll Transactions
|
||||||
stmt.execute("CREATE TABLE CreatePollTransactions (signature Signature, creator QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
|
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, "
|
+ "poll_name PollName NOT NULL, description VARCHAR(4000) NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "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
|
// 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, "
|
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)");
|
+ "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
|
// For the future: add flag to polls to allow one or multiple votes per voter
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 11:
|
case 11:
|
||||||
// Vote On Poll Transactions
|
// Vote On Poll Transactions
|
||||||
stmt.execute("CREATE TABLE VoteOnPollTransactions (signature Signature, voter QoraPublicKey NOT NULL, poll_name PollName NOT NULL, "
|
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, "
|
+ "option_index PollOptionIndex NOT NULL, previous_option_index PollOptionIndex, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
// Arbitrary/Multi-payment Transaction Payments
|
// Arbitrary/Multi-payment Transaction Payments
|
||||||
stmt.execute("CREATE TABLE SharedTransactionPayments (signature Signature, recipient QoraAddress NOT NULL, "
|
stmt.execute("CREATE TABLE SharedTransactionPayments (signature Signature, recipient QoraAddress NOT NULL, "
|
||||||
+ "amount QoraAmount NOT NULL, asset_id AssetID 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)");
|
+ "PRIMARY KEY (signature, recipient, asset_id), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
// Arbitrary Transactions
|
// Arbitrary Transactions
|
||||||
stmt.execute("CREATE TABLE ArbitraryTransactions (signature Signature, sender QoraPublicKey NOT NULL, version TINYINT NOT NULL, "
|
stmt.execute("CREATE TABLE ArbitraryTransactions (signature Signature, sender QoraPublicKey NOT NULL, version TINYINT NOT NULL, "
|
||||||
+ "service TINYINT NOT NULL, data_hash DataHash NOT NULL, "
|
+ "service TINYINT NOT NULL, data_hash DataHash NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
// NB: Actual data payload stored elsewhere
|
// 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
|
// For the future: data payload should be encrypted, at the very least with transaction's reference as the seed for the encryption key
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 14:
|
case 14:
|
||||||
// Issue Asset Transactions
|
// Issue Asset Transactions
|
||||||
stmt.execute(
|
stmt.execute(
|
||||||
"CREATE TABLE IssueAssetTransactions (signature Signature, issuer QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, "
|
"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, "
|
+ "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)");
|
+ "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
|
// For the future: maybe convert quantity from BIGINT to QoraAmount, regardless of divisibility
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 15:
|
case 15:
|
||||||
// Transfer Asset Transactions
|
// Transfer Asset Transactions
|
||||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||||
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL,"
|
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL,"
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
// Create Asset Order Transactions
|
// Create Asset Order Transactions
|
||||||
stmt.execute("CREATE TABLE CreateAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
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, "
|
+ "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)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 17:
|
case 17:
|
||||||
// Cancel Asset Order Transactions
|
// Cancel Asset Order Transactions
|
||||||
stmt.execute("CREATE TABLE CancelAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
stmt.execute("CREATE TABLE CancelAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||||
+ "asset_order_id AssetOrderID NOT NULL, "
|
+ "asset_order_id AssetOrderID NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 18:
|
case 18:
|
||||||
// Multi-payment Transactions
|
// Multi-payment Transactions
|
||||||
stmt.execute("CREATE TABLE MultiPaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, "
|
stmt.execute("CREATE TABLE MultiPaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 19:
|
case 19:
|
||||||
// Deploy CIYAM AT Transactions
|
// Deploy CIYAM AT Transactions
|
||||||
stmt.execute("CREATE TABLE DeployATTransactions (signature Signature, creator QoraPublicKey NOT NULL, AT_name ATName NOT NULL, "
|
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, "
|
+ "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, "
|
+ "creation_bytes VARBINARY(100000) NOT NULL, amount QoraAmount NOT NULL, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 20:
|
case 20:
|
||||||
// Message Transactions
|
// Message Transactions
|
||||||
stmt.execute(
|
stmt.execute(
|
||||||
"CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
"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, "
|
+ "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)");
|
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 21:
|
case 21:
|
||||||
// Assets (including QORA coin itself)
|
// Assets (including QORA coin itself)
|
||||||
stmt.execute(
|
stmt.execute("CREATE TABLE Assets (asset_id AssetID, owner QoraAddress NOT NULL, "
|
||||||
"CREATE TABLE Assets (asset_id AssetID IDENTITY, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, description VARCHAR(4000) 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)");
|
+ "quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, reference Signature NOT NULL, PRIMARY KEY (asset_id))");
|
||||||
// For when a user wants to lookup an asset by name
|
// We need a corresponding trigger to make sure new asset_id values are assigned sequentially
|
||||||
stmt.execute("CREATE INDEX AssetNameIndex on Assets (asset_name)");
|
stmt.execute(
|
||||||
break;
|
"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:
|
case 22:
|
||||||
// Accounts
|
// Accounts
|
||||||
stmt.execute("CREATE TABLE Accounts (account QoraAddress, reference Signature, PRIMARY KEY (account))");
|
stmt.execute("CREATE TABLE Accounts (account QoraAddress, reference Signature, PRIMARY KEY (account))");
|
||||||
stmt.execute(
|
stmt.execute("CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, "
|
||||||
"CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, PRIMARY KEY (account, asset_id))");
|
+ "PRIMARY KEY (account, asset_id))");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 23:
|
case 23:
|
||||||
// Asset Orders
|
// Asset Orders
|
||||||
stmt.execute(
|
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, "
|
"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, "
|
+ "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))");
|
+ "ordered TIMESTAMP WITH TIME ZONE NOT NULL, is_closed BOOLEAN NOT NULL, is_fulfilled BOOLEAN NOT NULL, "
|
||||||
// For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
|
+ "PRIMARY KEY (asset_order_id))");
|
||||||
stmt.execute("CREATE INDEX AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled)");
|
// For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
|
||||||
// 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 AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled)");
|
||||||
stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
|
// For when a user wants to look up their current/historic orders. is_closed included so user can filter by active/inactive orders.
|
||||||
break;
|
stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
|
||||||
|
break;
|
||||||
|
|
||||||
case 24:
|
case 24:
|
||||||
// Asset Trades
|
// Asset Trades
|
||||||
stmt.execute("CREATE TABLE AssetTrades (initiating_order_id AssetOrderId NOT NULL, target_order_id AssetOrderId NOT NULL, "
|
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)");
|
+ "amount QoraAmount NOT NULL, price QoraAmount NOT NULL, traded TIMESTAMP WITH TIME ZONE NOT NULL)");
|
||||||
// For looking up historic trades based on orders
|
// For looking up historic trades based on orders
|
||||||
stmt.execute("CREATE INDEX AssetTradeBuyOrderIndex on AssetTrades (initiating_order_id, traded)");
|
stmt.execute("CREATE INDEX AssetTradeBuyOrderIndex on AssetTrades (initiating_order_id, traded)");
|
||||||
stmt.execute("CREATE INDEX AssetTradeSellOrderIndex on AssetTrades (target_order_id, traded)");
|
stmt.execute("CREATE INDEX AssetTradeSellOrderIndex on AssetTrades (target_order_id, traded)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 25:
|
case 25:
|
||||||
// Polls/Voting
|
// Polls/Voting
|
||||||
stmt.execute(
|
stmt.execute(
|
||||||
"CREATE TABLE Polls (poll_name PollName, description VARCHAR(4000) NOT NULL, creator QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
|
"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))");
|
+ "published TIMESTAMP WITH TIME ZONE NOT NULL, " + "PRIMARY KEY (poll_name))");
|
||||||
// Various options available on a poll
|
// Various options available on a poll
|
||||||
stmt.execute("CREATE TABLE PollOptions (poll_name PollName, option_index TINYINT NOT NULL, option_name PollOption, "
|
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)");
|
+ "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.
|
// 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, "
|
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)");
|
+ "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
|
// For when a user wants to lookup poll they own
|
||||||
stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
|
stmt.execute("CREATE INDEX PollOwnerIndex on Polls (owner)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 26:
|
case 26:
|
||||||
// Registered Names
|
// Registered Names
|
||||||
stmt.execute(
|
stmt.execute(
|
||||||
"CREATE TABLE Names (name RegisteredName, data VARCHAR(4000) NOT NULL, registrant QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, "
|
"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, "
|
+ "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))");
|
+ "PRIMARY KEY (name))");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// database was updated
|
// database was updated
|
||||||
|
@ -4,6 +4,7 @@ import java.math.BigDecimal;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
import data.naming.NameData;
|
import data.naming.NameData;
|
||||||
import repository.NameRepository;
|
import repository.NameRepository;
|
||||||
@ -19,19 +20,18 @@ public class HSQLDBNameRepository implements NameRepository {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NameData fromName(String name) throws DataException {
|
public NameData fromName(String name) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet resultSet = this.repository
|
.checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name)) {
|
||||||
.checkedExecute("SELECT registrant, owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] registrantPublicKey = resultSet.getBytes(1);
|
byte[] registrantPublicKey = resultSet.getBytes(1);
|
||||||
String owner = resultSet.getString(2);
|
String owner = resultSet.getString(2);
|
||||||
String data = resultSet.getString(3);
|
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
|
// 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();
|
Long updated = updatedTimestamp == null ? null : updatedTimestamp.getTime();
|
||||||
|
|
||||||
byte[] reference = resultSet.getBytes(6);
|
byte[] reference = resultSet.getBytes(6);
|
||||||
|
@ -6,6 +6,7 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import repository.AccountRepository;
|
import repository.AccountRepository;
|
||||||
import repository.AssetRepository;
|
import repository.AssetRepository;
|
||||||
@ -19,6 +20,8 @@ import repository.hsqldb.transaction.HSQLDBTransactionRepository;
|
|||||||
|
|
||||||
public class HSQLDBRepository implements Repository {
|
public class HSQLDBRepository implements Repository {
|
||||||
|
|
||||||
|
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||||
|
|
||||||
protected Connection connection;
|
protected Connection connection;
|
||||||
|
|
||||||
// NB: no visibility modifier so only callable from within same package
|
// 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
|
@Override
|
||||||
public void close() throws DataException {
|
public void close() throws DataException {
|
||||||
try {
|
try (Statement stmt = this.connection.createStatement()) {
|
||||||
// Diagnostic check for uncommitted changes
|
// 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?
|
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");
|
throw new DataException("Unable to check repository status during close");
|
||||||
|
|
||||||
ResultSet rs = stmt.getResultSet();
|
try (ResultSet resultSet = stmt.getResultSet()) {
|
||||||
if (rs == null || !rs.next())
|
if (resultSet == null || !resultSet.next())
|
||||||
throw new DataException("Unable to check repository status during close");
|
System.out.println("Unable to check repository status during close");
|
||||||
|
|
||||||
boolean inTransaction = rs.getBoolean(1);
|
boolean inTransaction = resultSet.getBoolean(1);
|
||||||
int transactionCount = rs.getInt(2);
|
int transactionCount = resultSet.getInt(2);
|
||||||
if (inTransaction && transactionCount != 0)
|
if (inTransaction && transactionCount != 0)
|
||||||
System.out.println("Uncommitted changes (" + transactionCount + ") during repository close");
|
System.out.println("Uncommitted changes (" + transactionCount + ") during repository close");
|
||||||
|
}
|
||||||
|
|
||||||
// give connection back to the pool
|
// give connection back to the pool
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
@ -115,9 +116,12 @@ public class HSQLDBRepository implements Repository {
|
|||||||
* @return ResultSet, or null if there are no found rows
|
* @return ResultSet, or null if there are no found rows
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("resource")
|
||||||
public ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
public ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement(sql);
|
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);
|
return this.checkedExecuteResultSet(preparedStatement, objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,12 +202,13 @@ public class HSQLDBRepository implements Repository {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public Long callIdentity() throws SQLException {
|
public Long callIdentity() throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
|
try (PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
|
||||||
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement);
|
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement)) {
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return resultSet.getLong(1);
|
return resultSet.getLong(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,12 +229,13 @@ public class HSQLDBRepository implements Repository {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public boolean exists(String tableName, String whereClause, Object... objects) 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");
|
try (PreparedStatement preparedStatement = this.connection.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " LIMIT 1");
|
||||||
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement, objects);
|
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement, objects)) {
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,8 +247,9 @@ public class HSQLDBRepository implements Repository {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public void delete(String tableName, String whereClause, Object... objects) throws SQLException {
|
public void delete(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement("DELETE FROM " + tableName + " WHERE " + whereClause);
|
try (PreparedStatement preparedStatement = this.connection.prepareStatement("DELETE FROM " + tableName + " WHERE " + whereClause)) {
|
||||||
this.checkedExecuteUpdateCount(preparedStatement, objects);
|
this.checkedExecuteUpdateCount(preparedStatement, objects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package repository.hsqldb;
|
|||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.hsqldb.jdbc.JDBCPool;
|
import org.hsqldb.jdbc.JDBCPool;
|
||||||
|
|
||||||
@ -22,6 +23,14 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
|||||||
this.connectionPool = new JDBCPool();
|
this.connectionPool = new JDBCPool();
|
||||||
this.connectionPool.setUrl(this.connectionUrl);
|
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?
|
// Perform DB updates?
|
||||||
try (final Connection connection = this.connectionPool.getConnection()) {
|
try (final Connection connection = this.connectionPool.getConnection()) {
|
||||||
HSQLDBDatabaseUpdates.updateDatabase(connection);
|
HSQLDBDatabaseUpdates.updateDatabase(connection);
|
||||||
@ -30,6 +39,7 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Repository getRepository() throws DataException {
|
public Repository getRepository() throws DataException {
|
||||||
try {
|
try {
|
||||||
return new HSQLDBRepository(this.getConnection());
|
return new HSQLDBRepository(this.getConnection());
|
||||||
@ -41,22 +51,23 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
|||||||
private Connection getConnection() throws SQLException {
|
private Connection getConnection() throws SQLException {
|
||||||
Connection connection = this.connectionPool.getConnection();
|
Connection connection = this.connectionPool.getConnection();
|
||||||
|
|
||||||
// start transaction
|
// Set transaction level
|
||||||
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close() throws DataException {
|
public void close() throws DataException {
|
||||||
try {
|
try {
|
||||||
// Close all existing connections immediately
|
// Close all existing connections immediately
|
||||||
this.connectionPool.close(0);
|
this.connectionPool.close(0);
|
||||||
|
|
||||||
// Now that all connections are closed, create a dedicated connection to shut down repository
|
// Now that all connections are closed, create a dedicated connection to shut down repository
|
||||||
Connection connection = DriverManager.getConnection(this.connectionUrl);
|
try (Connection connection = DriverManager.getConnection(this.connectionUrl)) {
|
||||||
connection.createStatement().execute("SHUTDOWN");
|
connection.createStatement().execute("SHUTDOWN");
|
||||||
connection.close();
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Error during repository shutdown", e);
|
throw new DataException("Error during repository shutdown", e);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@ package repository.hsqldb;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,11 +58,11 @@ public class HSQLDBSaver {
|
|||||||
*/
|
*/
|
||||||
public boolean execute(HSQLDBRepository repository) throws SQLException {
|
public boolean execute(HSQLDBRepository repository) throws SQLException {
|
||||||
String sql = this.formatInsertWithPlaceholders();
|
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) {
|
for (int i = 0; i < this.objects.size(); ++i) {
|
||||||
Object object = this.objects.get(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) {
|
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 + 1, (BigDecimal) object);
|
||||||
preparedStatement.setBigDecimal(i + this.objects.size() + 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 {
|
} else {
|
||||||
preparedStatement.setObject(i + 1, object);
|
preparedStatement.setObject(i + 1, object);
|
||||||
preparedStatement.setObject(i + this.objects.size() + 1, object);
|
preparedStatement.setObject(i + this.objects.size() + 1, object);
|
||||||
|
@ -4,6 +4,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import data.voting.PollData;
|
import data.voting.PollData;
|
||||||
@ -22,36 +23,39 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
|
@Override
|
||||||
public PollData fromPollName(String pollName) throws DataException {
|
public PollData fromPollName(String pollName) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT description, creator, owner, published FROM Polls WHERE poll_name = ?", pollName)) {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT description, creator, owner, published FROM Polls WHERE poll_name = ?", pollName);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String description = resultSet.getString(1);
|
String description = resultSet.getString(1);
|
||||||
byte[] creatorPublicKey = resultSet.getBytes(2);
|
byte[] creatorPublicKey = resultSet.getBytes(2);
|
||||||
String owner = resultSet.getString(3);
|
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);
|
try (ResultSet optionsResultSet = this.repository
|
||||||
if (resultSet == null)
|
.checkedExecute("SELECT option_name FROM PollOptions where poll_name = ? ORDER BY option_index ASC", pollName)) {
|
||||||
return null;
|
if (optionsResultSet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
||||||
|
|
||||||
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
||||||
do {
|
do {
|
||||||
String optionName = resultSet.getString(1);
|
String optionName = optionsResultSet.getString(1);
|
||||||
|
|
||||||
pollOptions.add(new PollOptionData(optionName));
|
pollOptions.add(new PollOptionData(optionName));
|
||||||
} while (resultSet.next());
|
} while (optionsResultSet.next());
|
||||||
|
|
||||||
return new PollData(creatorPublicKey, owner, pollName, description, pollOptions, published);
|
return new PollData(creatorPublicKey, owner, pollName, description, pollOptions, published);
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch poll from repository", e);
|
throw new DataException("Unable to fetch poll from repository", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean pollExists(String pollName) throws DataException {
|
public boolean pollExists(String pollName) throws DataException {
|
||||||
try {
|
try {
|
||||||
return this.repository.exists("Polls", "poll_name = ?", pollName);
|
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 {
|
public void save(PollData pollData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Polls");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Polls");
|
||||||
|
|
||||||
@ -89,6 +94,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(String pollName) throws DataException {
|
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
|
// NOTE: The corresponding rows in PollOptions are deleted automatically by the database thanks to "ON DELETE CASCADE" in the PollOptions' FOREIGN KEY
|
||||||
// definition.
|
// definition.
|
||||||
@ -101,11 +107,11 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
|
|
||||||
// Votes
|
// Votes
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<VoteOnPollData> getVotes(String pollName) throws DataException {
|
public List<VoteOnPollData> getVotes(String pollName) throws DataException {
|
||||||
List<VoteOnPollData> votes = new ArrayList<VoteOnPollData>();
|
List<VoteOnPollData> votes = new ArrayList<VoteOnPollData>();
|
||||||
|
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT voter, option_index FROM PollVotes WHERE poll_name = ?", pollName)) {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT voter, option_index FROM PollVotes WHERE poll_name = ?", pollName);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return votes;
|
return votes;
|
||||||
|
|
||||||
@ -123,10 +129,10 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException {
|
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
|
voterPublicKey)) {
|
||||||
voterPublicKey);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -138,6 +144,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void save(VoteOnPollData voteOnPollData) throws DataException {
|
public void save(VoteOnPollData voteOnPollData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("PollVotes");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("PollVotes");
|
||||||
|
|
||||||
@ -151,6 +158,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void delete(String pollName, byte[] voterPublicKey) throws DataException {
|
public void delete(String pollName, byte[] voterPublicKey) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.delete("PollVotes", "poll_name = ? AND voter = ?", pollName, voterPublicKey);
|
this.repository.delete("PollVotes", "poll_name = ? AND voter = ?", pollName, voterPublicKey);
|
||||||
|
@ -20,16 +20,15 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT sender, version, service, data_hash from ArbitraryTransactions WHERE signature = ?",
|
signature)) {
|
||||||
signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] senderPublicKey = rs.getBytes(1);
|
byte[] senderPublicKey = resultSet.getBytes(1);
|
||||||
int version = rs.getInt(2);
|
int version = resultSet.getInt(2);
|
||||||
int service = rs.getInt(3);
|
int service = resultSet.getInt(3);
|
||||||
byte[] dataHash = rs.getBytes(4);
|
byte[] dataHash = resultSet.getBytes(4);
|
||||||
|
|
||||||
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
|
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
|
||||||
|
|
||||||
@ -51,7 +50,8 @@ public class HSQLDBArbitraryTransactionRepository extends HSQLDBTransactionRepos
|
|||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("ArbitraryTransactions");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("ArbitraryTransactions");
|
||||||
|
|
||||||
saveHelper.bind("signature", arbitraryTransactionData.getSignature()).bind("sender", arbitraryTransactionData.getSenderPublicKey())
|
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 {
|
try {
|
||||||
saveHelper.execute(this.repository);
|
saveHelper.execute(this.repository);
|
||||||
|
@ -17,16 +17,15 @@ public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] buyerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] buyerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?",
|
signature)) {
|
||||||
signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String name = rs.getString(1);
|
String name = resultSet.getString(1);
|
||||||
BigDecimal amount = rs.getBigDecimal(2);
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
String seller = rs.getString(3);
|
String seller = resultSet.getString(3);
|
||||||
byte[] nameReference = rs.getBytes(4);
|
byte[] nameReference = resultSet.getBytes(4);
|
||||||
|
|
||||||
return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, signature);
|
return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,12 +17,11 @@ public class HSQLDBCancelOrderTransactionRepository extends HSQLDBTransactionRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT asset_order_id FROM CancelAssetOrderTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT asset_order_id FROM CancelAssetOrderTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] assetOrderId = rs.getBytes(1);
|
byte[] assetOrderId = resultSet.getBytes(1);
|
||||||
|
|
||||||
return new CancelOrderTransactionData(creatorPublicKey, assetOrderId, fee, timestamp, reference, signature);
|
return new CancelOrderTransactionData(creatorPublicKey, assetOrderId, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,12 +17,11 @@ public class HSQLDBCancelSellNameTransactionRepository extends HSQLDBTransaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name FROM CancelSellNameTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT name FROM CancelSellNameTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String name = rs.getString(1);
|
String name = resultSet.getString(1);
|
||||||
|
|
||||||
return new CancelSellNameTransactionData(ownerPublicKey, name, fee, timestamp, reference, signature);
|
return new CancelSellNameTransactionData(ownerPublicKey, name, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,16 +17,15 @@ public class HSQLDBCreateOrderTransactionRepository extends HSQLDBTransactionRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet rs = this.repository
|
.checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature)) {
|
||||||
.checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
long haveAssetId = rs.getLong(1);
|
long haveAssetId = resultSet.getLong(1);
|
||||||
BigDecimal amount = rs.getBigDecimal(2);
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
long wantAssetId = rs.getLong(3);
|
long wantAssetId = resultSet.getLong(3);
|
||||||
BigDecimal price = rs.getBigDecimal(4);
|
BigDecimal price = resultSet.getBigDecimal(4);
|
||||||
|
|
||||||
return new CreateOrderTransactionData(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, signature);
|
return new CreateOrderTransactionData(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -20,30 +20,31 @@ public class HSQLDBCreatePollTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, poll_name, description FROM CreatePollTransactions WHERE signature = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT owner, poll_name, description FROM CreatePollTransactions WHERE signature = ?", signature);
|
signature)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String owner = rs.getString(1);
|
String owner = resultSet.getString(1);
|
||||||
String pollName = rs.getString(2);
|
String pollName = resultSet.getString(2);
|
||||||
String description = rs.getString(3);
|
String description = resultSet.getString(3);
|
||||||
|
|
||||||
rs = this.repository.checkedExecute("SELECT option_name FROM CreatePollTransactionOptions where signature = ? ORDER BY option_index ASC",
|
try (ResultSet optionsResultSet = this.repository
|
||||||
signature);
|
.checkedExecute("SELECT option_name FROM CreatePollTransactionOptions where signature = ? ORDER BY option_index ASC", signature)) {
|
||||||
if (rs == null)
|
if (optionsResultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
||||||
|
|
||||||
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
||||||
do {
|
do {
|
||||||
String optionName = rs.getString(1);
|
String optionName = optionsResultSet.getString(1);
|
||||||
|
|
||||||
pollOptions.add(new PollOptionData(optionName));
|
pollOptions.add(new PollOptionData(optionName));
|
||||||
} while (rs.next());
|
} 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) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch create poll transaction from repository", e);
|
throw new DataException("Unable to fetch create poll transaction from repository", e);
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,12 @@ public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String recipient = rs.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
BigDecimal amount = rs.getBigDecimal(2).setScale(8);
|
BigDecimal amount = resultSet.getBigDecimal(2).setScale(8);
|
||||||
|
|
||||||
return new GenesisTransactionData(recipient, amount, timestamp, signature);
|
return new GenesisTransactionData(recipient, amount, timestamp, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,20 +17,18 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
ResultSet rs = this.repository.checkedExecute(
|
"SELECT issuer, owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?", signature)) {
|
||||||
"SELECT issuer, owner, asset_name, description, quantity, is_divisible, asset_id FROM IssueAssetTransactions WHERE signature = ?",
|
if (resultSet == null)
|
||||||
signature);
|
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] issuerPublicKey = rs.getBytes(1);
|
byte[] issuerPublicKey = resultSet.getBytes(1);
|
||||||
String owner = rs.getString(2);
|
String owner = resultSet.getString(2);
|
||||||
String assetName = rs.getString(3);
|
String assetName = resultSet.getString(3);
|
||||||
String description = rs.getString(4);
|
String description = resultSet.getString(4);
|
||||||
long quantity = rs.getLong(5);
|
long quantity = resultSet.getLong(5);
|
||||||
boolean isDivisible = rs.getBoolean(6);
|
boolean isDivisible = resultSet.getBoolean(6);
|
||||||
Long assetId = rs.getLong(7);
|
Long assetId = resultSet.getLong(7);
|
||||||
|
|
||||||
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
|
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
|
||||||
signature);
|
signature);
|
||||||
|
@ -17,20 +17,19 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
ResultSet rs = this.repository.checkedExecute(
|
"SELECT version, sender, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature)) {
|
||||||
"SELECT version, sender, recipient, is_text, is_encrypted, amount, asset_id, data FROM MessageTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
int version = rs.getInt(1);
|
int version = resultSet.getInt(1);
|
||||||
byte[] senderPublicKey = rs.getBytes(2);
|
byte[] senderPublicKey = resultSet.getBytes(2);
|
||||||
String recipient = rs.getString(3);
|
String recipient = resultSet.getString(3);
|
||||||
boolean isText = rs.getBoolean(4);
|
boolean isText = resultSet.getBoolean(4);
|
||||||
boolean isEncrypted = rs.getBoolean(5);
|
boolean isEncrypted = resultSet.getBoolean(5);
|
||||||
BigDecimal amount = rs.getBigDecimal(6);
|
BigDecimal amount = resultSet.getBigDecimal(6);
|
||||||
Long assetId = rs.getLong(7);
|
Long assetId = resultSet.getLong(7);
|
||||||
byte[] data = rs.getBytes(8);
|
byte[] data = resultSet.getBytes(8);
|
||||||
|
|
||||||
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
|
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
|
||||||
signature);
|
signature);
|
||||||
|
@ -19,12 +19,11 @@ public class HSQLDBMultiPaymentTransactionRepository extends HSQLDBTransactionRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] senderPublicKey = rs.getBytes(1);
|
byte[] senderPublicKey = resultSet.getBytes(1);
|
||||||
|
|
||||||
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
|
List<PaymentData> payments = this.getPaymentsFromSignature(signature);
|
||||||
|
|
||||||
|
@ -17,14 +17,13 @@ public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] senderPublicKey = rs.getBytes(1);
|
byte[] senderPublicKey = resultSet.getBytes(1);
|
||||||
String recipient = rs.getString(2);
|
String recipient = resultSet.getString(2);
|
||||||
BigDecimal amount = rs.getBigDecimal(3);
|
BigDecimal amount = resultSet.getBigDecimal(3);
|
||||||
|
|
||||||
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
|
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,14 +17,13 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] registrantPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] registrantPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String owner = rs.getString(1);
|
String owner = resultSet.getString(1);
|
||||||
String name = rs.getString(2);
|
String name = resultSet.getString(2);
|
||||||
String data = rs.getString(3);
|
String data = resultSet.getString(3);
|
||||||
|
|
||||||
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
|
return new RegisterNameTransactionData(registrantPublicKey, owner, name, data, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,13 +17,12 @@ public class HSQLDBSellNameTransactionRepository extends HSQLDBTransactionReposi
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT name, amount FROM SellNameTransactions WHERE signature = ?", signature)) {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT name, amount FROM SellNameTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String name = rs.getString(1);
|
String name = resultSet.getString(1);
|
||||||
BigDecimal amount = rs.getBigDecimal(2);
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
|
|
||||||
return new SellNameTransactionData(ownerPublicKey, name, amount, fee, timestamp, reference, signature);
|
return new SellNameTransactionData(ownerPublicKey, name, amount, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -5,6 +5,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import data.PaymentData;
|
import data.PaymentData;
|
||||||
@ -59,17 +60,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
protected HSQLDBTransactionRepository() {
|
protected HSQLDBTransactionRepository() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TransactionData fromSignature(byte[] signature) throws DataException {
|
public TransactionData fromSignature(byte[] signature) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
|
signature)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
TransactionType type = TransactionType.valueOf(rs.getInt(1));
|
TransactionType type = TransactionType.valueOf(resultSet.getInt(1));
|
||||||
byte[] reference = rs.getBytes(2);
|
byte[] reference = resultSet.getBytes(2);
|
||||||
byte[] creatorPublicKey = rs.getBytes(3);
|
byte[] creatorPublicKey = resultSet.getBytes(3);
|
||||||
long timestamp = rs.getTimestamp(4).getTime();
|
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
|
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
|
||||||
|
|
||||||
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -77,17 +79,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TransactionData fromReference(byte[] reference) throws DataException {
|
public TransactionData fromReference(byte[] reference) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?", reference);
|
reference)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
TransactionType type = TransactionType.valueOf(rs.getInt(1));
|
TransactionType type = TransactionType.valueOf(resultSet.getInt(1));
|
||||||
byte[] signature = rs.getBytes(2);
|
byte[] signature = resultSet.getBytes(2);
|
||||||
byte[] creatorPublicKey = rs.getBytes(3);
|
byte[] creatorPublicKey = resultSet.getBytes(3);
|
||||||
long timestamp = rs.getTimestamp(4).getTime();
|
long timestamp = resultSet.getTimestamp(4, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
BigDecimal fee = rs.getBigDecimal(5).setScale(8);
|
BigDecimal fee = resultSet.getBigDecimal(5).setScale(8);
|
||||||
|
|
||||||
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
return this.fromBase(type, signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -152,21 +155,21 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<PaymentData> getPaymentsFromSignature(byte[] signature) throws DataException {
|
protected List<PaymentData> getPaymentsFromSignature(byte[] signature) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM SharedTransactionPayments WHERE signature = ?",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT recipient, amount, asset_id FROM SharedTransactionPayments WHERE signature = ?", signature);
|
signature)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
List<PaymentData> payments = new ArrayList<PaymentData>();
|
List<PaymentData> payments = new ArrayList<PaymentData>();
|
||||||
|
|
||||||
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
||||||
do {
|
do {
|
||||||
String recipient = rs.getString(1);
|
String recipient = resultSet.getString(1);
|
||||||
BigDecimal amount = rs.getBigDecimal(2);
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
long assetId = rs.getLong(3);
|
long assetId = resultSet.getLong(3);
|
||||||
|
|
||||||
payments.add(new PaymentData(recipient, assetId, amount));
|
payments.add(new PaymentData(recipient, assetId, amount));
|
||||||
} while (rs.next());
|
} while (resultSet.next());
|
||||||
|
|
||||||
return payments;
|
return payments;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -194,16 +197,15 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
if (signature == null)
|
if (signature == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// in one go?
|
// Fetch height using join via block's transactions
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
ResultSet rs = this.repository.checkedExecute(
|
"SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
|
||||||
"SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
|
signature)) {
|
||||||
signature);
|
|
||||||
|
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return rs.getInt(1);
|
return resultSet.getInt(1);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch transaction's height from repository", e);
|
throw new DataException("Unable to fetch transaction's height from repository", e);
|
||||||
}
|
}
|
||||||
@ -215,12 +217,12 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Fetch block signature (if any)
|
// Fetch block signature (if any)
|
||||||
try {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1",
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1", signature);
|
signature)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] blockSignature = rs.getBytes(1);
|
byte[] blockSignature = resultSet.getBytes(1);
|
||||||
|
|
||||||
return this.repository.getBlockRepository().fromSignature(blockSignature);
|
return this.repository.getBlockRepository().fromSignature(blockSignature);
|
||||||
} catch (SQLException | DataException e) {
|
} catch (SQLException | DataException e) {
|
||||||
|
@ -17,16 +17,15 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT sender, recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?",
|
.checkedExecute("SELECT sender, recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?", signature)) {
|
||||||
signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] senderPublicKey = rs.getBytes(1);
|
byte[] senderPublicKey = resultSet.getBytes(1);
|
||||||
String recipient = rs.getString(2);
|
String recipient = resultSet.getString(2);
|
||||||
long assetId = rs.getLong(3);
|
long assetId = resultSet.getLong(3);
|
||||||
BigDecimal amount = rs.getBigDecimal(4);
|
BigDecimal amount = resultSet.getBigDecimal(4);
|
||||||
|
|
||||||
return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature);
|
return new TransferAssetTransactionData(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,15 +17,15 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] ownerPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature);
|
.checkedExecute("SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?", signature)) {
|
||||||
if (rs == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String newOwner = rs.getString(1);
|
String newOwner = resultSet.getString(1);
|
||||||
String name = rs.getString(2);
|
String name = resultSet.getString(2);
|
||||||
String newData = rs.getString(3);
|
String newData = resultSet.getString(3);
|
||||||
byte[] nameReference = rs.getBytes(4);
|
byte[] nameReference = resultSet.getBytes(4);
|
||||||
|
|
||||||
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, signature);
|
return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -17,15 +17,14 @@ public class HSQLDBVoteOnPollTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
try {
|
try (ResultSet resultSet = this.repository
|
||||||
ResultSet rs = this.repository
|
.checkedExecute("SELECT poll_name, option_index, previous_option_index FROM VoteOnPollTransactions WHERE signature = ?", signature)) {
|
||||||
.checkedExecute("SELECT poll_name, option_index, previous_option_index FROM VoteOnPollTransactions WHERE signature = ?", signature);
|
if (resultSet == null)
|
||||||
if (rs == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String pollName = rs.getString(1);
|
String pollName = resultSet.getString(1);
|
||||||
int optionIndex = rs.getInt(2);
|
int optionIndex = resultSet.getInt(2);
|
||||||
Integer previousOptionIndex = rs.getInt(3);
|
Integer previousOptionIndex = resultSet.getInt(3);
|
||||||
|
|
||||||
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
|
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -10,7 +10,8 @@ import repository.hsqldb.HSQLDBRepositoryFactory;
|
|||||||
|
|
||||||
public class Common {
|
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
|
@BeforeClass
|
||||||
public static void setRepository() throws DataException {
|
public static void setRepository() throws DataException {
|
||||||
|
@ -35,15 +35,16 @@ public class RepositoryTests extends Common {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAccessAfterClose() throws DataException {
|
public void testAccessAfterClose() throws DataException {
|
||||||
Repository repository = RepositoryManager.getRepository();
|
try (Repository repository = RepositoryManager.getRepository()) {
|
||||||
assertNotNull(repository);
|
assertNotNull(repository);
|
||||||
|
|
||||||
repository.close();
|
repository.close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
repository.discardChanges();
|
repository.discardChanges();
|
||||||
fail();
|
fail();
|
||||||
} catch (NullPointerException | DataException e) {
|
} catch (NullPointerException | DataException e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,17 +18,16 @@ public class PaymentTransformer extends Transformer {
|
|||||||
// Property lengths
|
// Property lengths
|
||||||
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
||||||
private static final int ASSET_ID_LENGTH = LONG_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;
|
private static final int TOTAL_LENGTH = RECIPIENT_LENGTH + ASSET_ID_LENGTH + AMOUNT_LENGTH;
|
||||||
|
|
||||||
public static PaymentData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
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);
|
String recipient = Serialization.deserializeAddress(byteBuffer);
|
||||||
|
|
||||||
long assetId = byteBuffer.getLong();
|
long assetId = byteBuffer.getLong();
|
||||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
|
||||||
|
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH);
|
||||||
|
|
||||||
return new PaymentData(recipient, assetId, amount);
|
return new PaymentData(recipient, assetId, amount);
|
||||||
}
|
}
|
||||||
@ -42,8 +41,10 @@ public class PaymentTransformer extends Transformer {
|
|||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
Serialization.serializeAddress(bytes, paymentData.getRecipient());
|
Serialization.serializeAddress(bytes, paymentData.getRecipient());
|
||||||
|
|
||||||
bytes.write(Longs.toByteArray(paymentData.getAssetId()));
|
bytes.write(Longs.toByteArray(paymentData.getAssetId()));
|
||||||
Serialization.serializeBigDecimal(bytes, paymentData.getAmount());
|
|
||||||
|
Serialization.serializeBigDecimal(bytes, paymentData.getAmount(), AMOUNT_LENGTH);
|
||||||
|
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
} catch (IOException | ClassCastException e) {
|
} catch (IOException | ClassCastException e) {
|
||||||
|
@ -79,10 +79,12 @@ public class BlockTransformer extends Transformer {
|
|||||||
byteBuffer.get(reference);
|
byteBuffer.get(reference);
|
||||||
|
|
||||||
BigDecimal generatingBalance = BigDecimal.valueOf(byteBuffer.getLong()).setScale(8);
|
BigDecimal generatingBalance = BigDecimal.valueOf(byteBuffer.getLong()).setScale(8);
|
||||||
|
|
||||||
byte[] generatorPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
byte[] generatorPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||||
|
|
||||||
byte[] transactionsSignature = new byte[TRANSACTIONS_SIGNATURE_LENGTH];
|
byte[] transactionsSignature = new byte[TRANSACTIONS_SIGNATURE_LENGTH];
|
||||||
byteBuffer.get(transactionsSignature);
|
byteBuffer.get(transactionsSignature);
|
||||||
|
|
||||||
byte[] generatorSignature = new byte[GENERATOR_SIGNATURE_LENGTH];
|
byte[] generatorSignature = new byte[GENERATOR_SIGNATURE_LENGTH];
|
||||||
byteBuffer.get(generatorSignature);
|
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.
|
// Parse transactions now, compared to deferred parsing in Gen1, so we can throw ParseException if need be.
|
||||||
List<TransactionData> transactions = new ArrayList<TransactionData>();
|
List<TransactionData> transactions = new ArrayList<TransactionData>();
|
||||||
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
for (int t = 0; t < transactionCount; ++t) {
|
for (int t = 0; t < transactionCount; ++t) {
|
||||||
if (byteBuffer.remaining() < TRANSACTION_SIZE_LENGTH)
|
if (byteBuffer.remaining() < TRANSACTION_SIZE_LENGTH)
|
||||||
throw new TransformationException("Byte data too short for Block Transaction length");
|
throw new TransformationException("Byte data too short for Block Transaction length");
|
||||||
|
|
||||||
int transactionLength = byteBuffer.getInt();
|
int transactionLength = byteBuffer.getInt();
|
||||||
|
|
||||||
if (byteBuffer.remaining() < transactionLength)
|
if (byteBuffer.remaining() < transactionLength)
|
||||||
throw new TransformationException("Byte data too short for Block Transaction");
|
throw new TransformationException("Byte data too short for Block Transaction");
|
||||||
|
|
||||||
if (transactionLength > Block.MAX_BLOCK_BYTES)
|
if (transactionLength > Block.MAX_BLOCK_BYTES)
|
||||||
throw new TransformationException("Byte data too long for Block Transaction");
|
throw new TransformationException("Byte data too long for Block Transaction");
|
||||||
|
|
||||||
@ -293,7 +298,7 @@ public class BlockTransformer extends Transformer {
|
|||||||
|
|
||||||
for (Transaction transaction : block.getTransactions()) {
|
for (Transaction transaction : block.getTransactions()) {
|
||||||
if (!transaction.isSignatureValid())
|
if (!transaction.isSignatureValid())
|
||||||
return null;
|
throw new TransformationException("Transaction signature invalid when building block's transactions signature");
|
||||||
|
|
||||||
bytes.write(transaction.getTransactionData().getSignature());
|
bytes.write(transaction.getTransactionData().getSignature());
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
import org.json.simple.JSONArray;
|
||||||
@ -16,6 +17,7 @@ import com.google.common.primitives.Longs;
|
|||||||
|
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
|
import qora.block.BlockChain;
|
||||||
import qora.transaction.ArbitraryTransaction;
|
import qora.transaction.ArbitraryTransaction;
|
||||||
import data.PaymentData;
|
import data.PaymentData;
|
||||||
import data.transaction.ArbitraryTransactionData;
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||||
|
@ -155,7 +155,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
|||||||
// Special v1 version
|
// Special v1 version
|
||||||
|
|
||||||
// Replace transaction type with incorrect Register Name value
|
// 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());
|
System.out.println(HashCode.fromBytes(bytes).toString());
|
||||||
|
|
||||||
|
@ -131,8 +131,8 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
|||||||
// Special v1 version
|
// Special v1 version
|
||||||
|
|
||||||
// Zero duplicate signature/reference
|
// Zero duplicate signature/reference
|
||||||
int start = bytes.length - TransactionTransformer.SIGNATURE_LENGTH - TransactionTransformer.BIG_DECIMAL_LENGTH;
|
int start = bytes.length - SIGNATURE_LENGTH - BIG_DECIMAL_LENGTH;
|
||||||
int end = start + TransactionTransformer.SIGNATURE_LENGTH;
|
int end = start + SIGNATURE_LENGTH;
|
||||||
Arrays.fill(bytes, start, end, (byte) 0);
|
Arrays.fill(bytes, start, end, (byte) 0);
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
import org.json.simple.JSONArray;
|
||||||
@ -16,6 +17,7 @@ import com.google.common.primitives.Longs;
|
|||||||
|
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
|
import qora.block.BlockChain;
|
||||||
import data.PaymentData;
|
import data.PaymentData;
|
||||||
import data.transaction.MultiPaymentTransactionData;
|
import data.transaction.MultiPaymentTransactionData;
|
||||||
import transform.PaymentTransformer;
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||||
|
@ -36,27 +36,27 @@ public class v1feeder extends Thread {
|
|||||||
private static final int DEFAULT_PORT = 9084;
|
private static final int DEFAULT_PORT = 9084;
|
||||||
|
|
||||||
private static final int MAGIC_LENGTH = 4;
|
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 HAS_ID_LENGTH = 1;
|
||||||
private static final int ID_LENGTH = 4;
|
// private static final int ID_LENGTH = 4;
|
||||||
private static final int DATA_SIZE_LENGTH = 4;
|
// private static final int DATA_SIZE_LENGTH = 4;
|
||||||
private static final int CHECKSUM_LENGTH = 4;
|
private static final int CHECKSUM_LENGTH = 4;
|
||||||
|
|
||||||
private static final int SIGNATURE_LENGTH = 128;
|
private static final int SIGNATURE_LENGTH = 128;
|
||||||
|
|
||||||
private static final byte[] MAINNET_MAGIC = { 0x12, 0x34, 0x56, 0x78 };
|
private static final byte[] MAINNET_MAGIC = { 0x12, 0x34, 0x56, 0x78 };
|
||||||
|
|
||||||
private static final int GET_PEERS_TYPE = 1;
|
// private static final int GET_PEERS_TYPE = 1;
|
||||||
private static final int PEERS_TYPE = 2;
|
// private static final int PEERS_TYPE = 2;
|
||||||
private static final int HEIGHT_TYPE = 3;
|
private static final int HEIGHT_TYPE = 3;
|
||||||
private static final int GET_SIGNATURES_TYPE = 4;
|
private static final int GET_SIGNATURES_TYPE = 4;
|
||||||
private static final int SIGNATURES_TYPE = 5;
|
private static final int SIGNATURES_TYPE = 5;
|
||||||
private static final int GET_BLOCK_TYPE = 6;
|
private static final int GET_BLOCK_TYPE = 6;
|
||||||
private static final int BLOCK_TYPE = 7;
|
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 PING_TYPE = 9;
|
||||||
private static final int VERSION_TYPE = 10;
|
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 Socket socket;
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
@ -237,7 +237,7 @@ public class v1feeder extends Thread {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VERSION_TYPE:
|
case VERSION_TYPE:
|
||||||
long timestamp = byteBuffer.getLong();
|
@SuppressWarnings("unused") long timestamp = byteBuffer.getLong();
|
||||||
int versionLength = byteBuffer.getInt();
|
int versionLength = byteBuffer.getInt();
|
||||||
byte[] versionBytes = new byte[versionLength];
|
byte[] versionBytes = new byte[versionLength];
|
||||||
byteBuffer.get(versionBytes);
|
byteBuffer.get(versionBytes);
|
||||||
@ -299,6 +299,7 @@ public class v1feeder extends Thread {
|
|||||||
return newBufferEnd;
|
return newBufferEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user