forked from Qortal/qortal
More database work
No need for DB.executeUsingBytes as it was only a specific use-case for DB.checkedExecute. Callers now refactored to use DB.checkedExecute instead. Minor tidying up of BlockTransactions in light of above. In the HSQLDB database, asset keys/IDs are now "asset_id" (previously: "asset"). Added initial Asset/Order/Trade classes. Added CreateOrderTransaction class. Renamed some asset-related fields back to old gen1 names, e.g. haveAmount -> amount, wantAmount -> price. Added Accounts and AccountBalances to database. Added get/set confirmed balance support to Account. Added get/set last reference support to Account. Added Block.toJSON() - untested at this time. Fleshed out some Transaction sub-classes' process() and orphan() methods. Fleshed out PaymentTransaction.isValid(). Added Transaction.delete() - untested.
This commit is contained in:
parent
216ed7c772
commit
4ce499c444
@ -1,8 +1,8 @@
|
||||
package database;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@ -121,6 +121,7 @@ public class DB {
|
||||
* @return byte[length]
|
||||
*/
|
||||
public static byte[] getResultSetBytes(InputStream inputStream, int length) {
|
||||
// inputStream could be null if database's column's value is null
|
||||
if (inputStream == null)
|
||||
return null;
|
||||
|
||||
@ -145,6 +146,7 @@ public class DB {
|
||||
public static byte[] getResultSetBytes(InputStream inputStream) {
|
||||
final int BYTE_BUFFER_LENGTH = 1024;
|
||||
|
||||
// inputStream could be null if database's column's value is null
|
||||
if (inputStream == null)
|
||||
return null;
|
||||
|
||||
@ -168,33 +170,27 @@ public class DB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute SQL using byte[] as 1st placeholder.
|
||||
* Execute SQL and return ResultSet with but added checking.
|
||||
* <p>
|
||||
* <b>Note: calls ResultSet.next()</b> therefore returned ResultSet is already pointing to first row.
|
||||
* <p>
|
||||
* Typically used to fetch Blocks or Transactions using signature or reference.
|
||||
*
|
||||
* @param sql
|
||||
* @param bytes
|
||||
* @return ResultSet, or null if no matching rows found
|
||||
* @param objects
|
||||
* @return ResultSet, or null if there are no found rows
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static ResultSet executeUsingBytes(String sql, byte[] bytes) throws SQLException {
|
||||
public static ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
preparedStatement.setBinaryStream(1, new ByteArrayInputStream(bytes));
|
||||
for (int i = 0; i < objects.length; ++i)
|
||||
// Special treatment for BigDecimals so that they retain their "scale",
|
||||
// which would otherwise be assumed as 0.
|
||||
if (objects[i] instanceof BigDecimal)
|
||||
preparedStatement.setBigDecimal(i + 1, (BigDecimal) objects[i]);
|
||||
else
|
||||
preparedStatement.setObject(i + 1, objects[i]);
|
||||
|
||||
if (!preparedStatement.execute())
|
||||
throw new SQLException("Fetching from database produced no results");
|
||||
|
||||
ResultSet rs = preparedStatement.getResultSet();
|
||||
if (rs == null)
|
||||
throw new SQLException("Fetching results from database produced no ResultSet");
|
||||
|
||||
if (!rs.next())
|
||||
return null;
|
||||
|
||||
return rs;
|
||||
return checkedExecute(preparedStatement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,8 +206,8 @@ public class DatabaseUpdates {
|
||||
case 12:
|
||||
// Arbitrary/Multi-payment Transaction Payments
|
||||
stmt.execute("CREATE TABLE SharedTransactionPayments (signature Signature, recipient QoraPublicKey NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, asset AssetID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature, recipient, asset), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
+ "amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature, recipient, asset_id), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 13:
|
||||
@ -230,14 +230,14 @@ public class DatabaseUpdates {
|
||||
case 15:
|
||||
// Transfer Asset Transactions
|
||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "asset 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)");
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// Create Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CreateAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "have_asset AssetID NOT NULL, have_amount QoraAmount NOT NULL, want_asset AssetID NOT NULL, want_amount 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)");
|
||||
break;
|
||||
|
||||
@ -265,20 +265,22 @@ public class DatabaseUpdates {
|
||||
case 20:
|
||||
// Message Transactions
|
||||
stmt.execute("CREATE TABLE MessageTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, amount QoraAmount NOT NULL, asset 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)");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
// Assets (including QORA coin itself)
|
||||
stmt.execute(
|
||||
"CREATE TABLE Assets (asset AssetID IDENTITY, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, description VARCHAR(4000) NOT NULL, "
|
||||
"CREATE TABLE Assets (asset_id AssetID IDENTITY, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, description VARCHAR(4000) NOT NULL, "
|
||||
+ "quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, reference Signature NOT NULL)");
|
||||
break;
|
||||
|
||||
case 22:
|
||||
// Accounts
|
||||
stmt.execute("CREATE TABLE AccountBalances (account QoraAddress, asset AssetID, amount QoraAmount NOT NULL, PRIMARY KEY (account, asset))");
|
||||
stmt.execute("CREATE TABLE Accounts (account QoraAddress, reference Signature, PRIMARY KEY (account))");
|
||||
stmt.execute(
|
||||
"CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, PRIMARY KEY (account, asset_id))");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -2,6 +2,11 @@ package qora.account;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import database.DB;
|
||||
import database.SaveHelper;
|
||||
|
||||
public class Account {
|
||||
|
||||
@ -28,39 +33,60 @@ public class Account {
|
||||
return this.getAddress().equals(((Account) b).getAddress());
|
||||
}
|
||||
|
||||
// Balance manipulations - "key" is asset ID, or 0 for QORA
|
||||
// Balance manipulations - assetId is 0 for QORA
|
||||
|
||||
public BigDecimal getBalance(long key, int confirmations) {
|
||||
public BigDecimal getBalance(long assetId, int confirmations) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public BigDecimal getUnconfirmedBalance(long key) {
|
||||
public BigDecimal getUnconfirmedBalance(long assetId) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public BigDecimal getConfirmedBalance(long key) {
|
||||
// TODO
|
||||
return null;
|
||||
public BigDecimal getConfirmedBalance(long assetId) throws SQLException {
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||
if (resultSet == null)
|
||||
return BigDecimal.ZERO.setScale(8);
|
||||
|
||||
return resultSet.getBigDecimal(1);
|
||||
}
|
||||
|
||||
public void setConfirmedBalance(Connection connection, long key, BigDecimal amount) {
|
||||
// TODO
|
||||
return;
|
||||
public void setConfirmedBalance(Connection connection, long assetId, BigDecimal balance) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "AccountBalances");
|
||||
saveHelper.bind("account", this.getAddress()).bind("asset_id", assetId).bind("balance", balance);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
// Reference manipulations
|
||||
|
||||
public byte[] getLastReference() {
|
||||
// TODO
|
||||
return null;
|
||||
/**
|
||||
* Fetch last reference for account.
|
||||
*
|
||||
* @return byte[] reference, or null if no reference or account not found.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public byte[] getLastReference() throws SQLException {
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", this.getAddress());
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
return DB.getResultSetBytes(resultSet.getBinaryStream(1));
|
||||
}
|
||||
|
||||
// pass null to remove
|
||||
public void setLastReference(Connection connection, byte[] reference) {
|
||||
// TODO
|
||||
return;
|
||||
/**
|
||||
* Set last reference for account.
|
||||
*
|
||||
* @param connection
|
||||
* @param reference
|
||||
* -- null allowed
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void setLastReference(Connection connection, byte[] reference) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Accounts");
|
||||
saveHelper.bind("account", this.getAddress()).bind("reference", reference);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,12 +7,20 @@ import database.DB;
|
||||
import database.SaveHelper;
|
||||
import qora.account.PublicKeyAccount;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Probably need to standardize on using assetId or assetKey for the long value, and plain "asset" for the java object.
|
||||
* Thus in the database the primary key column could be called "asset_id".
|
||||
* In the Order object, we'd pass longs to variables with names like "haveAssetId" and use getters like "getHaveAssetId"
|
||||
* which frees up other method names like "getHaveAsset" to return a java Asset object.
|
||||
*/
|
||||
|
||||
public class Asset {
|
||||
|
||||
public static final long QORA = 0L;
|
||||
|
||||
// Properties
|
||||
private Long key;
|
||||
private Long assetId;
|
||||
private PublicKeyAccount owner;
|
||||
private String name;
|
||||
private String description;
|
||||
@ -20,8 +28,9 @@ public class Asset {
|
||||
private boolean isDivisible;
|
||||
private byte[] reference;
|
||||
|
||||
public Asset(Long key, PublicKeyAccount owner, String name, String description, long quantity, boolean isDivisible, byte[] reference) {
|
||||
this.key = key;
|
||||
// NOTE: key is Long because it can be null if asset ID/key not yet assigned (which is done by save() method).
|
||||
public Asset(Long assetId, PublicKeyAccount owner, String name, String description, long quantity, boolean isDivisible, byte[] reference) {
|
||||
this.assetId = assetId;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@ -30,6 +39,7 @@ public class Asset {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
// New asset with unassigned assetId
|
||||
public Asset(PublicKeyAccount owner, String name, String description, long quantity, boolean isDivisible, byte[] reference) {
|
||||
this(null, owner, name, description, quantity, isDivisible, reference);
|
||||
}
|
||||
@ -38,11 +48,11 @@ public class Asset {
|
||||
|
||||
public void save(Connection connection) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Assets");
|
||||
saveHelper.bind("asset", this.key).bind("owner", this.owner.getAddress()).bind("asset_name", this.name).bind("description", this.description)
|
||||
saveHelper.bind("asset_id", this.assetId).bind("owner", this.owner.getAddress()).bind("asset_name", this.name).bind("description", this.description)
|
||||
.bind("quantity", this.quantity).bind("is_divisible", this.isDivisible).bind("reference", this.reference);
|
||||
saveHelper.execute();
|
||||
|
||||
if (this.key == null)
|
||||
this.key = DB.callIdentity(connection);
|
||||
if (this.assetId == null)
|
||||
this.assetId = DB.callIdentity(connection);
|
||||
}
|
||||
}
|
||||
|
141
src/qora/assets/Order.java
Normal file
141
src/qora/assets/Order.java
Normal file
@ -0,0 +1,141 @@
|
||||
package qora.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import qora.account.Account;
|
||||
import utils.ParseException;
|
||||
|
||||
public class Order implements Comparable<Order> {
|
||||
|
||||
// Properties
|
||||
private BigInteger id;
|
||||
private Account creator;
|
||||
private long haveAssetId;
|
||||
private long wantAssetId;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal price;
|
||||
private long timestamp;
|
||||
|
||||
// Other properties
|
||||
private BigDecimal fulfilled;
|
||||
|
||||
// Constructors
|
||||
|
||||
public Order(BigInteger id, Account creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this.id = id;
|
||||
this.creator = creator;
|
||||
this.haveAssetId = haveAssetId;
|
||||
this.wantAssetId = wantAssetId;
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
|
||||
this.fulfilled = BigDecimal.ZERO.setScale(8);
|
||||
}
|
||||
|
||||
public Order(BigInteger id, Account creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price,
|
||||
long timestamp) {
|
||||
this(id, creator, haveAssetId, wantAssetId, amount, price, timestamp);
|
||||
|
||||
this.fulfilled = fulfilled;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public BigInteger getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Account getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
public long getHaveAssetId() {
|
||||
return haveAssetId;
|
||||
}
|
||||
|
||||
public long getWantAssetId() {
|
||||
return wantAssetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public BigDecimal getFulfilled() {
|
||||
return fulfilled;
|
||||
}
|
||||
|
||||
public void setFulfilled(BigDecimal fulfilled) {
|
||||
this.fulfilled = fulfilled;
|
||||
}
|
||||
|
||||
// More information
|
||||
|
||||
public BigDecimal getAmountLeft() {
|
||||
return this.amount.subtract(this.fulfilled);
|
||||
}
|
||||
|
||||
public boolean isFulfilled() {
|
||||
return this.fulfilled.compareTo(this.amount) == 0;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// public List<Trade> getInitiatedTrades() {}
|
||||
|
||||
// TODO
|
||||
// public boolean isConfirmed() {}
|
||||
|
||||
// Load/Save/Delete
|
||||
|
||||
// Navigation
|
||||
|
||||
// XXX is this getInitiatedTrades() above?
|
||||
public List<Trade> getTrades() {
|
||||
// TODO
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Converters
|
||||
|
||||
public static Order parse(byte[] data) throws ParseException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
// TODO
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
// Other
|
||||
|
||||
@Override
|
||||
public int compareTo(Order order) {
|
||||
// Compare using prices
|
||||
return this.price.compareTo(order.getPrice());
|
||||
}
|
||||
|
||||
public Order copy() {
|
||||
try {
|
||||
return parse(this.toBytes());
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
47
src/qora/assets/Trade.java
Normal file
47
src/qora/assets/Trade.java
Normal file
@ -0,0 +1,47 @@
|
||||
package qora.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Trade {
|
||||
|
||||
// Properties
|
||||
private BigInteger initiator;
|
||||
private BigInteger target;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal price;
|
||||
private long timestamp;
|
||||
|
||||
// Constructors
|
||||
|
||||
public Trade(BigInteger initiator, BigInteger target, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this.initiator = initiator;
|
||||
this.target = target;
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public BigInteger getInitiator() {
|
||||
return initiator;
|
||||
}
|
||||
|
||||
public BigInteger getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
}
|
@ -12,9 +12,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import database.DB;
|
||||
@ -23,8 +26,13 @@ import database.SaveHelper;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.assets.Order;
|
||||
import qora.assets.Trade;
|
||||
import qora.transaction.CreateOrderTransaction;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.TransactionFactory;
|
||||
import utils.Base58;
|
||||
import utils.ParseException;
|
||||
|
||||
/*
|
||||
* Typical use-case scenarios:
|
||||
@ -215,11 +223,11 @@ public class Block {
|
||||
// Allocate cache for results
|
||||
this.transactions = new ArrayList<Transaction>();
|
||||
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", this.getSignature());
|
||||
ResultSet rs = DB.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ?", this.getSignature());
|
||||
if (rs == null)
|
||||
return this.transactions; // No transactions in this block
|
||||
|
||||
// NB: do-while loop because DB.executeUsingBytes() implicitly calls ResultSet.next() for us
|
||||
// NB: do-while loop because DB.checkedExecute() implicitly calls ResultSet.next() for us
|
||||
do {
|
||||
byte[] transactionSignature = DB.getResultSetBytes(rs.getBinaryStream(1), Transaction.SIGNATURE_LENGTH);
|
||||
this.transactions.add(TransactionFactory.fromSignature(transactionSignature));
|
||||
@ -233,7 +241,7 @@ public class Block {
|
||||
// Load/Save
|
||||
|
||||
protected Block(byte[] signature) throws SQLException {
|
||||
this(DB.executeUsingBytes("SELECT " + DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature));
|
||||
this(DB.checkedExecute("SELECT " + DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature));
|
||||
}
|
||||
|
||||
protected Block(ResultSet rs) throws SQLException {
|
||||
@ -329,7 +337,7 @@ public class Block {
|
||||
if (blockSignature == null)
|
||||
return null;
|
||||
|
||||
ResultSet resultSet = DB.executeUsingBytes("SELECT " + DB_COLUMNS + " FROM Blocks WHERE reference = ?", blockSignature);
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT " + DB_COLUMNS + " FROM Blocks WHERE reference = ?", blockSignature);
|
||||
|
||||
try {
|
||||
return new Block(resultSet);
|
||||
@ -340,12 +348,95 @@ public class Block {
|
||||
|
||||
// Converters
|
||||
|
||||
public JSONObject toJSON() {
|
||||
// TODO
|
||||
return null;
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONObject toJSON() throws SQLException {
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
json.put("version", this.version);
|
||||
json.put("timestamp", this.timestamp);
|
||||
json.put("generatingBalance", this.generatingBalance);
|
||||
json.put("generator", this.generator.getAddress());
|
||||
json.put("generatorPublicKey", Base58.encode(this.generator.getPublicKey()));
|
||||
json.put("fee", this.getTotalFees().toPlainString());
|
||||
json.put("transactionsSignature", Base58.encode(this.transactionsSignature));
|
||||
json.put("generatorSignature", Base58.encode(this.generatorSignature));
|
||||
json.put("signature", Base58.encode(this.getSignature()));
|
||||
|
||||
if (this.reference != null)
|
||||
json.put("reference", Base58.encode(this.reference));
|
||||
|
||||
json.put("height", this.getHeight());
|
||||
|
||||
// Add transaction info
|
||||
JSONArray transactionsJson = new JSONArray();
|
||||
boolean tradesHappened = false;
|
||||
|
||||
for (Transaction transaction : this.getTransactions()) {
|
||||
transactionsJson.add(transaction.toJSON());
|
||||
|
||||
// If this is an asset CreateOrderTransaction then check to see if any trades happened
|
||||
if (transaction.getType() == Transaction.TransactionType.CREATE_ASSET_ORDER) {
|
||||
CreateOrderTransaction orderTransaction = (CreateOrderTransaction) transaction;
|
||||
Order order = orderTransaction.getOrder();
|
||||
List<Trade> trades = order.getTrades();
|
||||
|
||||
// Filter out trades with timestamps that don't match order transaction's timestamp
|
||||
trades.removeIf((Trade trade) -> trade.getTimestamp() != order.getTimestamp());
|
||||
|
||||
// Any trades left?
|
||||
if (!trades.isEmpty()) {
|
||||
tradesHappened = true;
|
||||
|
||||
// No need to check any further
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
json.put("transactions", transactionsJson);
|
||||
|
||||
// Add asset trade activity flag
|
||||
json.put("assetTrades", tradesHappened);
|
||||
|
||||
// Add CIYAM AT info (if any)
|
||||
if (atBytes != null) {
|
||||
json.put("blockATs", HashCode.fromBytes(atBytes).toString());
|
||||
json.put("atFees", this.atFees);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
try {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength());
|
||||
bytes.write(Ints.toByteArray(this.version));
|
||||
bytes.write(Longs.toByteArray(this.timestamp));
|
||||
bytes.write(this.reference);
|
||||
// NOTE: generatingBalance serialized as long value, not as BigDecimal, for historic compatibility
|
||||
bytes.write(Longs.toByteArray(this.generatingBalance.longValue()));
|
||||
bytes.write(this.generator.getPublicKey());
|
||||
bytes.write(this.transactionsSignature);
|
||||
bytes.write(this.generatorSignature);
|
||||
|
||||
if (this.version >= 2) {
|
||||
if (this.atBytes != null) {
|
||||
bytes.write(Ints.toByteArray(this.atBytes.length));
|
||||
bytes.write(this.atBytes);
|
||||
// NOTE: atFees serialized as long value, not as BigDecimal, for historic compatibility
|
||||
bytes.write(Longs.toByteArray(this.atFees.longValue()));
|
||||
} else {
|
||||
bytes.write(Ints.toByteArray(0));
|
||||
bytes.write(Longs.toByteArray(0L));
|
||||
}
|
||||
}
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Block parse(byte[] data) throws ParseException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class BlockChain {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int getBlockHeightFromSignature(byte[] signature) throws SQLException {
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT height FROM Blocks WHERE signature = ?", signature);
|
||||
ResultSet rs = DB.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
|
@ -1,12 +1,9 @@
|
||||
package qora.block;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import database.DB;
|
||||
@ -50,13 +47,8 @@ public class BlockTransaction {
|
||||
|
||||
protected BlockTransaction(byte[] blockSignature, int sequence) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Can't use DB.executeUsingBytes() here as we need two placeholders
|
||||
PreparedStatement preparedStatement = connection
|
||||
.prepareStatement("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ? and sequence = ?");
|
||||
preparedStatement.setBinaryStream(1, new ByteArrayInputStream(blockSignature));
|
||||
preparedStatement.setInt(2, sequence);
|
||||
|
||||
ResultSet rs = DB.checkedExecute(preparedStatement);
|
||||
ResultSet rs = DB.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ? and sequence = ?", blockSignature,
|
||||
sequence);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
@ -67,7 +59,7 @@ public class BlockTransaction {
|
||||
}
|
||||
|
||||
protected BlockTransaction(byte[] transactionSignature) throws SQLException {
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT block_signature, sequence FROM BlockTransactions WHERE transaction_signature = ?", transactionSignature);
|
||||
ResultSet rs = DB.checkedExecute("SELECT block_signature, sequence FROM BlockTransactions WHERE transaction_signature = ?", transactionSignature);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
|
165
src/qora/transaction/CreateOrderTransaction.java
Normal file
165
src/qora/transaction/CreateOrderTransaction.java
Normal file
@ -0,0 +1,165 @@
|
||||
package qora.transaction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import database.DB;
|
||||
import database.NoDataFoundException;
|
||||
import database.SaveHelper;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.assets.Order;
|
||||
import utils.ParseException;
|
||||
|
||||
public class CreateOrderTransaction extends Transaction {
|
||||
|
||||
// Properties
|
||||
private Order order;
|
||||
|
||||
// Property lengths
|
||||
private static final int ASSET_LENGTH = 8;
|
||||
private static final int AMOUNT_LENGTH = 12;
|
||||
private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + (ASSET_LENGTH + AMOUNT_LENGTH) * 2;
|
||||
|
||||
// Constructors
|
||||
|
||||
public CreateOrderTransaction(PublicKeyAccount creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
||||
long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.CREATE_ASSET_ORDER, fee, creator, timestamp, reference, signature);
|
||||
|
||||
this.order = new Order(new BigInteger(this.signature), creator, haveAssetId, wantAssetId, amount, price, timestamp);
|
||||
}
|
||||
|
||||
public CreateOrderTransaction(PublicKeyAccount creator, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
||||
long timestamp, byte[] reference) {
|
||||
this(creator, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public Order getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
// More information
|
||||
|
||||
public int getDataLength() {
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH;
|
||||
}
|
||||
|
||||
// Load/Save
|
||||
|
||||
/**
|
||||
* Load CreateOrderTransaction from DB using signature.
|
||||
*
|
||||
* @param signature
|
||||
* @throws NoDataFoundException
|
||||
* if no matching row found
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected CreateOrderTransaction(byte[] signature) throws SQLException {
|
||||
super(TransactionType.CREATE_ASSET_ORDER, signature);
|
||||
|
||||
ResultSet rs = DB.checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateOrderTransactions WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
long haveAssetId = rs.getLong(1);
|
||||
BigDecimal amount = rs.getBigDecimal(2);
|
||||
long wantAssetId = rs.getLong(3);
|
||||
BigDecimal price = rs.getBigDecimal(4);
|
||||
|
||||
this.order = new Order(new BigInteger(this.signature), this.creator, haveAssetId, wantAssetId, amount, price, this.timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load CreateOrderTransaction from DB using signature
|
||||
*
|
||||
* @param signature
|
||||
* @return CreateOrderTransaction, or null if not found
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static CreateOrderTransaction fromSignature(byte[] signature) throws SQLException {
|
||||
try {
|
||||
return new CreateOrderTransaction(signature);
|
||||
} catch (NoDataFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "CreateAssetOrderTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("creator", this.creator.getPublicKey()).bind("have_asset_id", this.order.getHaveAssetId())
|
||||
.bind("amount", this.order.getAmount()).bind("want_asset_id", this.order.getWantAssetId()).bind("price", this.order.getPrice());
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
// Converters
|
||||
|
||||
protected static Transaction parse(ByteBuffer byteBuffer) throws ParseException {
|
||||
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
||||
throw new ParseException("Byte data too short for CreateOrderTransaction");
|
||||
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject toJSON() throws SQLException {
|
||||
JSONObject json = getBaseJSON();
|
||||
|
||||
// TODO
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
try {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength());
|
||||
bytes.write(Ints.toByteArray(this.type.value));
|
||||
bytes.write(Longs.toByteArray(this.timestamp));
|
||||
bytes.write(this.reference);
|
||||
|
||||
// TODO
|
||||
|
||||
bytes.write(this.signature);
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
// TODO
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
@ -21,8 +21,10 @@ import database.SaveHelper;
|
||||
import qora.account.Account;
|
||||
import qora.account.GenesisAccount;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.crypto.Crypto;
|
||||
import utils.Base58;
|
||||
import utils.ParseException;
|
||||
import utils.Serialization;
|
||||
|
||||
public class GenesisTransaction extends Transaction {
|
||||
@ -59,6 +61,7 @@ public class GenesisTransaction extends Transaction {
|
||||
|
||||
// More information
|
||||
|
||||
@Override
|
||||
public int getDataLength() {
|
||||
return TYPE_LENGTH + TYPELESS_LENGTH;
|
||||
}
|
||||
@ -76,7 +79,7 @@ public class GenesisTransaction extends Transaction {
|
||||
protected GenesisTransaction(byte[] signature) throws SQLException {
|
||||
super(TransactionType.GENESIS, signature);
|
||||
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
|
||||
ResultSet rs = DB.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
@ -110,9 +113,9 @@ public class GenesisTransaction extends Transaction {
|
||||
|
||||
// Converters
|
||||
|
||||
protected static Transaction parse(ByteBuffer byteBuffer) throws TransactionParseException {
|
||||
protected static Transaction parse(ByteBuffer byteBuffer) throws ParseException {
|
||||
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
||||
throw new TransactionParseException("Byte data too short for GenesisTransaction");
|
||||
throw new ParseException("Byte data too short for GenesisTransaction");
|
||||
|
||||
long timestamp = byteBuffer.getLong();
|
||||
String recipient = Serialization.deserializeRecipient(byteBuffer);
|
||||
@ -132,6 +135,7 @@ public class GenesisTransaction extends Transaction {
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toBytes() {
|
||||
try {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength());
|
||||
@ -189,6 +193,7 @@ public class GenesisTransaction extends Transaction {
|
||||
return Arrays.equals(this.signature, calcSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid(Connection connection) {
|
||||
// Check amount is zero or positive
|
||||
if (this.amount.compareTo(BigDecimal.ZERO) == -1)
|
||||
@ -201,19 +206,26 @@ public class GenesisTransaction extends Transaction {
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Connection connection) throws SQLException {
|
||||
// TODO
|
||||
this.save(connection);
|
||||
|
||||
// SET recipient's balance
|
||||
// this.recipient.setConfirmedBalance(this.amount, db);
|
||||
// Set recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.amount);
|
||||
|
||||
// Set recipient's reference
|
||||
// recipient.setLastReference(this.signature, db);
|
||||
recipient.setLastReference(connection, this.signature);
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) {
|
||||
// TODO
|
||||
@Override
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
|
||||
// Set recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, BigDecimal.ZERO);
|
||||
|
||||
// Set recipient's reference
|
||||
recipient.setLastReference(connection, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
@ -19,7 +20,10 @@ import database.NoDataFoundException;
|
||||
import database.SaveHelper;
|
||||
import qora.account.Account;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.crypto.Crypto;
|
||||
import utils.Base58;
|
||||
import utils.ParseException;
|
||||
import utils.Serialization;
|
||||
|
||||
public class PaymentTransaction extends Transaction {
|
||||
@ -82,7 +86,7 @@ public class PaymentTransaction extends Transaction {
|
||||
protected PaymentTransaction(byte[] signature) throws SQLException {
|
||||
super(TransactionType.PAYMENT, signature);
|
||||
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
|
||||
ResultSet rs = DB.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
@ -111,16 +115,16 @@ public class PaymentTransaction extends Transaction {
|
||||
super.save(connection);
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "PaymentTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("sender", this.sender.getPublicKey()).bind("recipient", this.recipient.getAddress()).bind("amount", this.amount);
|
||||
saveHelper.bind("signature", this.signature).bind("sender", this.sender.getPublicKey()).bind("recipient", this.recipient.getAddress()).bind("amount",
|
||||
this.amount);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
// Converters
|
||||
|
||||
protected static Transaction parse(ByteBuffer byteBuffer) throws TransactionParseException {
|
||||
// TODO
|
||||
protected static Transaction parse(ByteBuffer byteBuffer) throws ParseException {
|
||||
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
||||
throw new TransactionParseException("Byte data too short for PaymentTransaction");
|
||||
throw new ParseException("Byte data too short for PaymentTransaction");
|
||||
|
||||
long timestamp = byteBuffer.getLong();
|
||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||
@ -167,17 +171,67 @@ public class PaymentTransaction extends Transaction {
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) {
|
||||
// TODO
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
// Non-database checks first
|
||||
|
||||
// Check recipient is a valid address
|
||||
if (!Crypto.isValidAddress(this.recipient.getAddress()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check amount is positive
|
||||
if (this.amount.compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_AMOUNT;
|
||||
|
||||
// Check fee is positive
|
||||
if (this.fee.compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check reference is correct
|
||||
if (!Arrays.equals(this.sender.getLastReference(), this.reference))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check sender has enough funds
|
||||
if (this.sender.getBalance(Asset.QORA, 1).compareTo(this.amount.add(this.fee)) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) {
|
||||
// TODO
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
|
||||
// Update sender's balance
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).subtract(this.amount).subtract(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).add(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.signature);
|
||||
|
||||
// If recipient has no reference yet, then this is their starting reference
|
||||
if (this.recipient.getLastReference() == null)
|
||||
this.recipient.setLastReference(connection, this.signature);
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) {
|
||||
// TODO
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
|
||||
// Update sender's balance
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).add(this.amount).add(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).subtract(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.reference);
|
||||
|
||||
/*
|
||||
* If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which would have changed
|
||||
* their last reference) thus this is their first reference so remove it.
|
||||
*/
|
||||
if (Arrays.equals(this.recipient.getLastReference(), this.signature))
|
||||
this.recipient.setLastReference(connection, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import qora.block.BlockTransaction;
|
||||
import settings.Settings;
|
||||
|
||||
import utils.Base58;
|
||||
import utils.ParseException;
|
||||
|
||||
public abstract class Transaction {
|
||||
|
||||
@ -48,7 +49,7 @@ public abstract class Transaction {
|
||||
|
||||
// Validation results
|
||||
public enum ValidationResult {
|
||||
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3);
|
||||
OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6);
|
||||
|
||||
public final int value;
|
||||
|
||||
@ -201,7 +202,7 @@ public abstract class Transaction {
|
||||
return blockChainHeight - ourHeight + 1;
|
||||
}
|
||||
|
||||
// Load/Save
|
||||
// Load/Save/Delete
|
||||
|
||||
// Typically called by sub-class' load-from-DB constructors
|
||||
|
||||
@ -217,7 +218,7 @@ public abstract class Transaction {
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected Transaction(TransactionType type, byte[] signature) throws SQLException {
|
||||
ResultSet rs = DB.executeUsingBytes("SELECT reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
|
||||
ResultSet rs = DB.checkedExecute("SELECT reference, creator, creation, fee FROM Transactions WHERE signature = ? AND type = ?", signature, type.value);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
@ -238,6 +239,10 @@ public abstract class Transaction {
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
protected void delete(Connection connection) throws SQLException {
|
||||
DB.checkedExecute("DELETE FROM Transactions WHERE signature = ?", this.signature);
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
/**
|
||||
@ -285,12 +290,12 @@ public abstract class Transaction {
|
||||
|
||||
// Converters
|
||||
|
||||
public static Transaction parse(byte[] data) throws TransactionParseException {
|
||||
public static Transaction parse(byte[] data) throws ParseException {
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
if (data.length < TYPE_LENGTH)
|
||||
throw new TransactionParseException("Byte data too short to determine transaction type");
|
||||
throw new ParseException("Byte data too short to determine transaction type");
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
|
||||
|
||||
@ -325,9 +330,11 @@ public abstract class Transaction {
|
||||
json.put("type", this.type.value);
|
||||
json.put("fee", this.fee.toPlainString());
|
||||
json.put("timestamp", this.timestamp);
|
||||
json.put("signature", Base58.encode(this.signature));
|
||||
|
||||
if (this.reference != null)
|
||||
json.put("reference", Base58.encode(this.reference));
|
||||
json.put("signature", Base58.encode(this.signature));
|
||||
|
||||
json.put("confirmations", this.getConfirmations());
|
||||
|
||||
return json;
|
||||
@ -363,10 +370,10 @@ public abstract class Transaction {
|
||||
return this.creator.verify(this.signature, this.toBytesLessSignature());
|
||||
}
|
||||
|
||||
public abstract ValidationResult isValid(Connection connection);
|
||||
public abstract ValidationResult isValid(Connection connection) throws SQLException;
|
||||
|
||||
public abstract void process(Connection connection) throws SQLException;
|
||||
|
||||
public abstract void orphan(Connection connection);
|
||||
public abstract void orphan(Connection connection) throws SQLException;
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public class TransactionFactory {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Transaction fromSignature(byte[] signature) throws SQLException {
|
||||
ResultSet resultSet = DB.executeUsingBytes("SELECT type, signature FROM Transactions WHERE signature = ?", signature);
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT type, signature FROM Transactions WHERE signature = ?", signature);
|
||||
return fromResultSet(resultSet);
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ public class TransactionFactory {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Transaction fromReference(byte[] reference) throws SQLException {
|
||||
ResultSet resultSet = DB.executeUsingBytes("SELECT type, signature FROM Transactions WHERE reference = ?", reference);
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT type, signature FROM Transactions WHERE reference = ?", reference);
|
||||
return fromResultSet(resultSet);
|
||||
}
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
package qora.transaction;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class TransactionParseException extends Exception {
|
||||
|
||||
public TransactionParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -132,19 +132,19 @@ public class migrate extends common {
|
||||
PreparedStatement issueAssetPStmt = c.prepareStatement("INSERT INTO IssueAssetTransactions "
|
||||
+ formatWithPlaceholders("signature", "creator", "asset_name", "description", "quantity", "is_divisible"));
|
||||
PreparedStatement transferAssetPStmt = c
|
||||
.prepareStatement("INSERT INTO TransferAssetTransactions " + formatWithPlaceholders("signature", "sender", "recipient", "asset", "amount"));
|
||||
.prepareStatement("INSERT INTO TransferAssetTransactions " + formatWithPlaceholders("signature", "sender", "recipient", "asset_id", "amount"));
|
||||
PreparedStatement createAssetOrderPStmt = c.prepareStatement("INSERT INTO CreateAssetOrderTransactions "
|
||||
+ formatWithPlaceholders("signature", "creator", "have_asset", "have_amount", "want_asset", "want_amount"));
|
||||
+ formatWithPlaceholders("signature", "creator", "have_asset_id", "amount", "want_asset_id", "price"));
|
||||
PreparedStatement cancelAssetOrderPStmt = c
|
||||
.prepareStatement("INSERT INTO CancelAssetOrderTransactions " + formatWithPlaceholders("signature", "creator", "asset_order"));
|
||||
PreparedStatement multiPaymentPStmt = c.prepareStatement("INSERT INTO MultiPaymentTransactions " + formatWithPlaceholders("signature", "sender"));
|
||||
PreparedStatement deployATPStmt = c.prepareStatement("INSERT INTO DeployATTransactions "
|
||||
+ formatWithPlaceholders("signature", "creator", "AT_name", "description", "AT_type", "AT_tags", "creation_bytes", "amount"));
|
||||
PreparedStatement messagePStmt = c.prepareStatement("INSERT INTO MessageTransactions "
|
||||
+ formatWithPlaceholders("signature", "sender", "recipient", "is_text", "is_encrypted", "amount", "asset", "data"));
|
||||
+ formatWithPlaceholders("signature", "sender", "recipient", "is_text", "is_encrypted", "amount", "asset_id", "data"));
|
||||
|
||||
PreparedStatement sharedPaymentPStmt = c
|
||||
.prepareStatement("INSERT INTO SharedTransactionPayments " + formatWithPlaceholders("signature", "recipient", "amount", "asset"));
|
||||
.prepareStatement("INSERT INTO SharedTransactionPayments " + formatWithPlaceholders("signature", "recipient", "amount", "asset_id"));
|
||||
|
||||
PreparedStatement blockTxPStmt = c
|
||||
.prepareStatement("INSERT INTO BlockTransactions " + formatWithPlaceholders("block_signature", "sequence", "transaction_signature"));
|
||||
|
@ -14,12 +14,12 @@ import qora.block.Block;
|
||||
import qora.block.GenesisBlock;
|
||||
import qora.transaction.GenesisTransaction;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.TransactionParseException;
|
||||
import utils.ParseException;
|
||||
|
||||
public class transactions extends common {
|
||||
|
||||
@Test
|
||||
public void testGenesisSerialization() throws SQLException, TransactionParseException {
|
||||
public void testGenesisSerialization() throws SQLException, ParseException {
|
||||
GenesisBlock block = GenesisBlock.getInstance();
|
||||
|
||||
GenesisTransaction transaction = (GenesisTransaction) block.getTransactions().get(1);
|
||||
@ -36,7 +36,7 @@ public class transactions extends common {
|
||||
assertTrue(Arrays.equals(transaction.getSignature(), parsedTransaction.getSignature()));
|
||||
}
|
||||
|
||||
public void testGenericSerialization(Transaction transaction) throws SQLException, TransactionParseException {
|
||||
public void testGenericSerialization(Transaction transaction) throws SQLException, ParseException {
|
||||
assertNotNull(transaction);
|
||||
|
||||
byte[] bytes = transaction.toBytes();
|
||||
@ -47,7 +47,7 @@ public class transactions extends common {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaymentSerialization() throws SQLException, TransactionParseException {
|
||||
public void testPaymentSerialization() throws SQLException, ParseException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
|
10
src/utils/ParseException.java
Normal file
10
src/utils/ParseException.java
Normal file
@ -0,0 +1,10 @@
|
||||
package utils;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ParseException extends Exception {
|
||||
|
||||
public ParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user