diff --git a/src/main/java/org/qora/api/model/AggregatedOrder.java b/src/main/java/org/qora/api/model/AggregatedOrder.java index 29865361..f6323014 100644 --- a/src/main/java/org/qora/api/model/AggregatedOrder.java +++ b/src/main/java/org/qora/api/model/AggregatedOrder.java @@ -2,6 +2,7 @@ package org.qora.api.model; import java.math.BigDecimal; +import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; @@ -11,11 +12,18 @@ import org.qora.data.asset.OrderData; @XmlAccessorType(XmlAccessType.NONE) public class AggregatedOrder { + // Not exposed to API private OrderData orderData; + // Needed by JAXB for [un]marshalling protected AggregatedOrder() { } + public void beforeMarshal(Marshaller m) { + // OrderData needs to calculate values for us + this.orderData.beforeMarshal(m); + } + public AggregatedOrder(OrderData orderData) { this.orderData = orderData; } @@ -30,4 +38,19 @@ public class AggregatedOrder { return this.orderData.getAmount(); } + @XmlElement(name = "unfulfilledAssetId") + public long getUnfulfilledAssetId() { + return this.orderData.getAmountAssetId(); + } + + @XmlElement(name = "unfulfilledAssetName") + public String getUnfulfilledAssetName() { + return this.orderData.getAmountAssetName(); + } + + @XmlElement(name = "pricePair") + public String getPricePair() { + return this.orderData.getPricePair(); + } + } diff --git a/src/main/java/org/qora/api/resource/AssetsResource.java b/src/main/java/org/qora/api/resource/AssetsResource.java index 4c5bc47e..f61a623d 100644 --- a/src/main/java/org/qora/api/resource/AssetsResource.java +++ b/src/main/java/org/qora/api/resource/AssetsResource.java @@ -633,9 +633,9 @@ public class AssetsResource { } @GET - @Path("/transfers/{assetid}/{address}") + @Path("/transfers/{assetid}") @Operation( - summary = "Asset transfers for specific asset and address combination", + summary = "Asset transfers for specific asset, with optional address filter", responses = { @ApiResponse( description = "Asset transactions", @@ -654,7 +654,7 @@ public class AssetsResource { }) public List getAssetTransfers(@Parameter( ref = "assetid" - ) @PathParam("assetid") int assetId, @PathParam("address") String address, @Parameter( + ) @PathParam("assetid") int assetId, @QueryParam("address") String address, @Parameter( ref = "limit" ) @QueryParam("limit") Integer limit, @Parameter( ref = "offset" @@ -665,7 +665,7 @@ public class AssetsResource { if (!repository.getAssetRepository().assetExists(assetId)) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID); - if (!Crypto.isValidAddress(address)) + if (address != null && !Crypto.isValidAddress(address)) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); return repository.getTransactionRepository().getAssetTransfers(assetId, address, limit, offset, reverse); diff --git a/src/main/java/org/qora/data/asset/OrderData.java b/src/main/java/org/qora/data/asset/OrderData.java index 84189f2e..4833f288 100644 --- a/src/main/java/org/qora/data/asset/OrderData.java +++ b/src/main/java/org/qora/data/asset/OrderData.java @@ -2,12 +2,16 @@ package org.qora.data.asset; import java.math.BigDecimal; +import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import io.swagger.v3.oas.annotations.media.Schema; +import org.qora.crypto.Crypto; -// All properties to be converted to JSON via JAX-RS +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.AccessMode; + +// All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) public class OrderData implements Comparable { @@ -38,13 +42,59 @@ public class OrderData implements Comparable { @Schema(description = "has this order been fully traded?") private boolean isFulfilled; + // Used by API - not always present + + @Schema(accessMode = AccessMode.READ_ONLY) + private String creator; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String haveAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String wantAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private long amountAssetId; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String amountAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String pricePair; + // Constructors - // necessary for JAX-RS serialization + // Necessary for JAXB serialization protected OrderData() { } - public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price, long timestamp, boolean isClosed, boolean isFulfilled) { + // Called before converting to JSON for API + public void beforeMarshal(Marshaller m) { + if (this.creator == null && this.creatorPublicKey != null) + this.creator = Crypto.toAddress(this.creatorPublicKey); + + // If we don't have the extra asset name fields then we can't fill in the others + if (this.haveAssetName == null) + return; + + // 'old' pricing scheme is simpler so test for that first + // XXX TODO + + // 'new' pricing scheme + if (this.haveAssetId < this.wantAssetId) { + this.amountAssetId = this.wantAssetId; + this.amountAssetName = this.wantAssetName; + this.pricePair = this.haveAssetName + "/" + this.wantAssetName; + } else { + this.amountAssetId = this.haveAssetId; + this.amountAssetName = this.haveAssetName; + this.pricePair = this.wantAssetName + "/" + this.haveAssetName; + } + } + + /** Constructs OrderData using data from repository, including optional API fields. */ + public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price, long timestamp, + boolean isClosed, boolean isFulfilled, String haveAssetName, String wantAssetName) { this.orderId = orderId; this.creatorPublicKey = creatorPublicKey; this.haveAssetId = haveAssetId; @@ -55,9 +105,17 @@ public class OrderData implements Comparable { this.timestamp = timestamp; this.isClosed = isClosed; this.isFulfilled = isFulfilled; + + this.haveAssetName = haveAssetName; + this.wantAssetName = wantAssetName; } - /** Constructs OrderData using typical deserialized network data */ + /** Constructs OrderData using data from repository, excluding optional API fields. */ + public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price, long timestamp, boolean isClosed, boolean isFulfilled) { + this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled, null, null); + } + + /** Constructs OrderData using data typically received from network. */ public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) { this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, BigDecimal.ZERO.setScale(8), price, timestamp, false, false); } @@ -116,6 +174,28 @@ public class OrderData implements Comparable { this.isFulfilled = isFulfilled; } + // Some JAXB/API-related getters + + public String getHaveAssetName() { + return this.haveAssetName; + } + + public String getWantAssetName() { + return this.wantAssetName; + } + + public long getAmountAssetId() { + return this.amountAssetId; + } + + public String getAmountAssetName() { + return this.amountAssetName; + } + + public String getPricePair() { + return this.pricePair; + } + @Override public int compareTo(OrderData orderData) { // Compare using prices diff --git a/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java b/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java index 346c95f9..88d6078d 100644 --- a/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java +++ b/src/main/java/org/qora/data/transaction/CreateAssetOrderTransactionData.java @@ -2,13 +2,16 @@ package org.qora.data.transaction; import java.math.BigDecimal; +import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import org.qora.block.BlockChain; import org.qora.transaction.Transaction.TransactionType; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.AccessMode; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -25,6 +28,23 @@ public class CreateAssetOrderTransactionData extends TransactionData { @Schema(description = "price in lowest-assetID asset / highest-assetID asset") private BigDecimal price; + // Used by API - not always present + + @Schema(accessMode = AccessMode.READ_ONLY) + private String haveAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String wantAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private long amountAssetId; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String amountAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String pricePair; + // Constructors // For JAXB @@ -32,16 +52,53 @@ public class CreateAssetOrderTransactionData extends TransactionData { super(TransactionType.CREATE_ASSET_ORDER); } + // Called before converting to JSON for API + public void beforeMarshal(Marshaller m) { + final boolean isNewPricing = this.timestamp > BlockChain.getInstance().getNewAssetPricingTimestamp(); + + this.amountAssetId = (isNewPricing && this.haveAssetId < this.wantAssetId) ? this.wantAssetId : this.haveAssetId; + + // If we don't have the extra asset name fields then we can't fill in the others + if (this.haveAssetName == null) + return; + + if (isNewPricing) { + // 'new' pricing scheme + if (this.haveAssetId < this.wantAssetId) { + this.amountAssetName = this.wantAssetName; + this.pricePair = this.haveAssetName + "/" + this.wantAssetName; + } else { + this.amountAssetName = this.haveAssetName; + this.pricePair = this.wantAssetName + "/" + this.haveAssetName; + } + } else { + // 'old' pricing scheme is simpler + this.amountAssetName = this.haveAssetName; + this.pricePair = this.wantAssetName + "/" + this.haveAssetName; + } + } + + /** Constructs using data from repository, including optional asset names. */ public CreateAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, - BigDecimal amount, BigDecimal price, BigDecimal fee, byte[] signature) { + BigDecimal amount, BigDecimal price, BigDecimal fee, String haveAssetName, String wantAssetName, byte[] signature) { super(TransactionType.CREATE_ASSET_ORDER, timestamp, txGroupId, reference, creatorPublicKey, fee, signature); this.haveAssetId = haveAssetId; this.wantAssetId = wantAssetId; this.amount = amount; this.price = price; + + this.haveAssetName = haveAssetName; + this.wantAssetName = wantAssetName; } + /** Constructs using data from repository, excluding optional asset names. */ + public CreateAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, + BigDecimal amount, BigDecimal price, BigDecimal fee, byte[] signature) { + this(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, null, null, signature); + } + + /** Constructor typically used with data from network. */ public CreateAssetOrderTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee) { this(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, null); diff --git a/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java b/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java index 9ee5b7b0..f9bb7776 100644 --- a/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java +++ b/src/main/java/org/qora/data/transaction/TransferAssetTransactionData.java @@ -9,6 +9,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import org.qora.transaction.Transaction.TransactionType; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.AccessMode; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -21,6 +22,10 @@ public class TransferAssetTransactionData extends TransactionData { private BigDecimal amount; private long assetId; + // Used by API - not always present + @Schema(accessMode = AccessMode.READ_ONLY) + protected String assetName; + // Constructors // For JAXB @@ -32,16 +37,25 @@ public class TransferAssetTransactionData extends TransactionData { this.creatorPublicKey = this.senderPublicKey; } + /** Constructs using data from repository, including optional assetName. */ public TransferAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount, - long assetId, BigDecimal fee, byte[] signature) { + long assetId, BigDecimal fee, String assetName, byte[] signature) { super(TransactionType.TRANSFER_ASSET, timestamp, txGroupId, reference, senderPublicKey, fee, signature); this.senderPublicKey = senderPublicKey; this.recipient = recipient; this.amount = amount; this.assetId = assetId; + this.assetName = assetName; } + /** Constructs using data from repository, excluding optional assetName. */ + public TransferAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount, + long assetId, BigDecimal fee, byte[] signature) { + this(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, assetId, fee, null, signature); + } + + /** Constructs using data typically received over network. */ public TransferAssetTransactionData(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee) { this(timestamp, txGroupId, reference, senderPublicKey, recipient, amount, assetId, fee, null); diff --git a/src/main/java/org/qora/repository/TransactionRepository.java b/src/main/java/org/qora/repository/TransactionRepository.java index 18b684c2..103a6174 100644 --- a/src/main/java/org/qora/repository/TransactionRepository.java +++ b/src/main/java/org/qora/repository/TransactionRepository.java @@ -63,7 +63,7 @@ public interface TransactionRepository { throws DataException; /** - * Returns list of TRANSFER_ASSET transactions relating to specific asset ID/address combination. + * Returns list of TRANSFER_ASSET transactions relating to specific asset ID, with optional address filter. * * @param assetId * @param address diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java index bd23232e..4a7073e1 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java @@ -6,6 +6,7 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -191,9 +192,13 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public OrderData fromOrderId(byte[] orderId) throws DataException { - try (ResultSet resultSet = this.repository.checkedExecute( - "SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled FROM AssetOrders WHERE asset_order_id = ?", - orderId)) { + String sql = "SELECT creator, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled, HaveAsset.asset_name, WantAsset.asset_name " + + "FROM AssetOrders " + + "JOIN Assets AS HaveAsset ON HaveAsset.asset_id = have_asset_id " + + "JOIN Assets AS WantAsset ON WantAsset.asset_id = want_asset_id " + + "WHERE asset_order_id = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, orderId)) { if (resultSet == null) return null; @@ -206,8 +211,10 @@ public class HSQLDBAssetRepository implements AssetRepository { long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); boolean isClosed = resultSet.getBoolean(8); boolean isFulfilled = resultSet.getBoolean(9); + String haveAssetName = resultSet.getString(10); + String wantAssetName = resultSet.getString(11); - return new OrderData(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled); + return new OrderData(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled, haveAssetName, wantAssetName); } catch (SQLException e) { throw new DataException("Unable to fetch asset order from repository", e); } @@ -216,16 +223,30 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getOpenOrders(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "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"; + List orders = new ArrayList(); + + // Cache have & want asset names for later use, which also saves a table join + AssetData haveAssetData = this.fromAssetId(haveAssetId); + if (haveAssetData == null) + return orders; + + AssetData wantAssetData = this.fromAssetId(wantAssetId); + if (wantAssetData == null) + return orders; + + String sql = "SELECT creator, asset_order_id, amount, fulfilled, price, ordered " + + "FROM AssetOrders " + + "WHERE have_asset_id = ? AND want_asset_id = ? AND NOT is_closed AND NOT is_fulfilled "; + + sql += "ORDER BY price"; if (reverse != null && reverse) sql += " DESC"; + sql += ", ordered"; if (reverse != null && reverse) sql += " DESC"; - sql += HSQLDBRepository.limitOffsetSql(limit, offset); - List orders = new ArrayList(); + sql += HSQLDBRepository.limitOffsetSql(limit, offset); try (ResultSet resultSet = this.repository.checkedExecute(sql, haveAssetId, wantAssetId)) { if (resultSet == null) @@ -242,7 +263,7 @@ public class HSQLDBAssetRepository implements AssetRepository { boolean isFulfilled = false; OrderData order = new OrderData(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, - price, timestamp, isClosed, isFulfilled); + price, timestamp, isClosed, isFulfilled, haveAssetData.getName(), wantAssetData.getName()); orders.add(order); } while (resultSet.next()); @@ -254,22 +275,24 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getOpenOrdersForTrading(long haveAssetId, long wantAssetId, BigDecimal minimumPrice) throws DataException { - Object[] bindParams; - String sql = "SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders " + List bindParams = new ArrayList<>(3); + + String sql = "SELECT creator, asset_order_id, amount, fulfilled, price, ordered " + + "FROM AssetOrders " + "WHERE have_asset_id = ? AND want_asset_id = ? AND NOT is_closed AND NOT is_fulfilled "; + Collections.addAll(bindParams, haveAssetId, wantAssetId); + if (minimumPrice != null) { sql += "AND price >= ? "; - bindParams = new Object[] {haveAssetId, wantAssetId, minimumPrice}; - } else { - bindParams = new Object[] {haveAssetId, wantAssetId}; + bindParams.add(minimumPrice); } sql += "ORDER BY price DESC, ordered"; List orders = new ArrayList(); - try (ResultSet resultSet = this.repository.checkedExecute(sql, bindParams)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, bindParams.toArray())) { if (resultSet == null) return orders; @@ -283,6 +306,7 @@ public class HSQLDBAssetRepository implements AssetRepository { boolean isClosed = false; boolean isFulfilled = false; + // We don't need asset names so we can use simpler constructor OrderData order = new OrderData(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed, isFulfilled); orders.add(order); @@ -297,13 +321,27 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getAggregatedOpenOrders(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "SELECT price, SUM(amount - fulfilled), MAX(ordered) FROM AssetOrders " - + "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE GROUP BY price ORDER BY price"; + List orders = new ArrayList(); + + // Cache have & want asset names for later use, which also saves a table join + AssetData haveAssetData = this.fromAssetId(haveAssetId); + if (haveAssetData == null) + return orders; + + AssetData wantAssetData = this.fromAssetId(wantAssetId); + if (wantAssetData == null) + return orders; + + String sql = "SELECT price, SUM(amount - fulfilled), MAX(ordered) " + + "FROM AssetOrders " + + "WHERE have_asset_id = ? AND want_asset_id = ? AND NOT is_closed AND NOT is_fulfilled " + + "GROUP BY price "; + + sql += "ORDER BY price"; if (reverse != null && reverse) sql += " DESC"; - sql += HSQLDBRepository.limitOffsetSql(limit, offset); - List orders = new ArrayList(); + sql += HSQLDBRepository.limitOffsetSql(limit, offset); try (ResultSet resultSet = this.repository.checkedExecute(sql, haveAssetId, wantAssetId)) { if (resultSet == null) @@ -315,7 +353,7 @@ public class HSQLDBAssetRepository implements AssetRepository { long timestamp = resultSet.getTimestamp(3).getTime(); OrderData order = new OrderData(null, null, haveAssetId, wantAssetId, totalUnfulfilled, BigDecimal.ZERO, - price, timestamp, false, false); + price, timestamp, false, false, haveAssetData.getName(), wantAssetData.getName()); orders.add(order); } while (resultSet.next()); @@ -328,15 +366,23 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getAccountsOrders(byte[] publicKey, Boolean optIsClosed, Boolean optIsFulfilled, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "SELECT asset_order_id, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled " - + "FROM AssetOrders WHERE creator = ?"; + // We have to join for have/want asset data as it might vary + String sql = "SELECT asset_order_id, have_asset_id, want_asset_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled, HaveAsset.asset_name, WantAsset.asset_name " + + "FROM AssetOrders " + + "JOIN Assets AS HaveAsset ON HaveAsset.asset_id = have_asset_id " + + "JOIN Assets AS WantAsset ON WantAsset.asset_id = want_asset_id " + + "WHERE creator = ?"; + if (optIsClosed != null) sql += " AND is_closed = " + (optIsClosed ? "TRUE" : "FALSE"); + if (optIsFulfilled != null) sql += " AND is_fulfilled = " + (optIsFulfilled ? "TRUE" : "FALSE"); + sql += " ORDER BY ordered"; if (reverse != null && reverse) sql += " DESC"; + sql += HSQLDBRepository.limitOffsetSql(limit, offset); List orders = new ArrayList(); @@ -355,9 +401,11 @@ public class HSQLDBAssetRepository implements AssetRepository { long timestamp = resultSet.getTimestamp(7, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); boolean isClosed = resultSet.getBoolean(8); boolean isFulfilled = resultSet.getBoolean(9); + String haveAssetName = resultSet.getString(10); + String wantAssetName = resultSet.getString(11); OrderData order = new OrderData(orderId, publicKey, haveAssetId, wantAssetId, amount, fulfilled, - price, timestamp, isClosed, isFulfilled); + price, timestamp, isClosed, isFulfilled, haveAssetName, wantAssetName); orders.add(order); } while (resultSet.next()); @@ -370,18 +418,32 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getAccountsOrders(byte[] publicKey, long haveAssetId, long wantAssetId, Boolean optIsClosed, Boolean optIsFulfilled, Integer limit, Integer offset, Boolean reverse) throws DataException { + List orders = new ArrayList(); + + // Cache have & want asset names for later use, which also saves a table join + AssetData haveAssetData = this.fromAssetId(haveAssetId); + if (haveAssetData == null) + return orders; + + AssetData wantAssetData = this.fromAssetId(wantAssetId); + if (wantAssetData == null) + return orders; + String sql = "SELECT asset_order_id, amount, fulfilled, price, ordered, is_closed, is_fulfilled " - + "FROM AssetOrders WHERE creator = ? AND have_asset_id = ? AND want_asset_id = ?"; + + "FROM AssetOrders " + + "WHERE creator = ? AND have_asset_id = ? AND want_asset_id = ?"; + if (optIsClosed != null) sql += " AND is_closed = " + (optIsClosed ? "TRUE" : "FALSE"); + if (optIsFulfilled != null) sql += " AND is_fulfilled = " + (optIsFulfilled ? "TRUE" : "FALSE"); + sql += " ORDER BY ordered"; if (reverse != null && reverse) sql += " DESC"; - sql += HSQLDBRepository.limitOffsetSql(limit, offset); - List orders = new ArrayList(); + sql += HSQLDBRepository.limitOffsetSql(limit, offset); try (ResultSet resultSet = this.repository.checkedExecute(sql, publicKey, haveAssetId, wantAssetId)) { if (resultSet == null) @@ -397,7 +459,7 @@ public class HSQLDBAssetRepository implements AssetRepository { boolean isFulfilled = resultSet.getBoolean(7); OrderData order = new OrderData(orderId, publicKey, haveAssetId, wantAssetId, amount, fulfilled, - price, timestamp, isClosed, isFulfilled); + price, timestamp, isClosed, isFulfilled, haveAssetData.getName(), wantAssetData.getName()); orders.add(order); } while (resultSet.next()); diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java index 1dc663a8..1c577b6f 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBCreateAssetOrderTransactionRepository.java @@ -17,8 +17,13 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti } TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException { - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT have_asset_id, amount, want_asset_id, price FROM CreateAssetOrderTransactions WHERE signature = ?", signature)) { + String sql = "SELECT have_asset_id, amount, want_asset_id, price, HaveAsset.asset_name, WantAsset.asset_name " + + "FROM CreateAssetOrderTransactions " + + "JOIN Assets AS HaveAsset ON HaveAsset.asset_id = have_asset_id " + + "JOIN Assets AS WantAsset ON WantAsset.asset_id = want_asset_id " + + "WHERE signature = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, signature)) { if (resultSet == null) return null; @@ -26,8 +31,10 @@ public class HSQLDBCreateAssetOrderTransactionRepository extends HSQLDBTransacti BigDecimal amount = resultSet.getBigDecimal(2); long wantAssetId = resultSet.getLong(3); BigDecimal price = resultSet.getBigDecimal(4); + String haveAssetName = resultSet.getString(5); + String wantAssetName = resultSet.getString(6); - return new CreateAssetOrderTransactionData(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, signature); + return new CreateAssetOrderTransactionData(timestamp, txGroupId, reference, creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, haveAssetName, wantAssetName, signature); } catch (SQLException e) { throw new DataException("Unable to fetch create order transaction from repository", e); } diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java index 855f1aec..5d71f794 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -535,18 +535,30 @@ public class HSQLDBTransactionRepository implements TransactionRepository { @Override public List getAssetTransfers(long assetId, String address, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "SELECT creation, tx_group_id, reference, fee, signature, sender, recipient, amount FROM TransferAssetTransactions " - + "JOIN Transactions USING (signature) " - + "JOIN Accounts ON public_key = sender " - + "WHERE asset_id = ? AND ? IN (account, recipient) " - + "ORDER by creation "; + List bindParams = new ArrayList<>(3); + + String sql = "SELECT creation, tx_group_id, reference, fee, signature, sender, recipient, amount, asset_name " + + "FROM TransferAssetTransactions JOIN Transactions USING (signature) "; + + if (address != null) + sql += "JOIN Accounts ON public_key = sender "; + + sql += "JOIN Assets USING (asset_id) WHERE asset_id = ? "; + bindParams.add(assetId); + + if (address != null) { + sql += "AND ? IN (account, recipient) "; + bindParams.add(address); + } + + sql += "ORDER by creation "; sql += (reverse == null || !reverse) ? "ASC" : "DESC"; sql += HSQLDBRepository.limitOffsetSql(limit, offset); List assetTransfers = new ArrayList<>(); - try (ResultSet resultSet = this.repository.checkedExecute(sql, assetId, address)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, bindParams.toArray())) { if (resultSet == null) return assetTransfers; @@ -559,8 +571,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository { byte[] creatorPublicKey = resultSet.getBytes(6); String recipient = resultSet.getString(7); BigDecimal amount = resultSet.getBigDecimal(8); + String assetName = resultSet.getString(9); - assetTransfers.add(new TransferAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, assetId, fee, signature)); + assetTransfers.add(new TransferAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, assetId, fee, assetName, signature)); } while (resultSet.next()); return assetTransfers; diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java index c11a503d..d81cd253 100644 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBTransferAssetTransactionRepository.java @@ -17,16 +17,18 @@ public class HSQLDBTransferAssetTransactionRepository extends HSQLDBTransactionR } TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException { - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT recipient, asset_id, amount FROM TransferAssetTransactions WHERE signature = ?", signature)) { + String sql = "SELECT recipient, asset_id, amount, asset_name FROM TransferAssetTransactions JOIN Assets USING (asset_id) WHERE signature = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, signature)) { if (resultSet == null) return null; String recipient = resultSet.getString(1); long assetId = resultSet.getLong(2); BigDecimal amount = resultSet.getBigDecimal(3); + String assetName = resultSet.getString(4); - return new TransferAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, assetId, fee, signature); + return new TransferAssetTransactionData(timestamp, txGroupId, reference, creatorPublicKey, recipient, amount, assetId, fee, assetName, signature); } catch (SQLException e) { throw new DataException("Unable to fetch transfer asset transaction from repository", e); }