forked from Qortal/qortal
Asset API additions, txGroupId minor fix, testnet blockgen fix
GET /assets/orderbook/{assetid}/{otherassetid} renamed to GET /assets/openorders/{assetid}/{otherassetid} Replacement /assets/orderbook/{assetid}/{otherassetid} now returns aggregated orders, with entries containing only "price" and "unfulfilled" (amount). Added /assets/orders/{assetid}/{otherassetid}/{address} to return orders by specific account, for a specific asset-pair. Block timestamp validity extracted to separate method so that BlockGenerator can test timestamp and generate blocks at the usual rate, even for testnets. This still allows testnets to a way to generate blocks on demand as Block's isValid skips some timestamp validity checks if testnet. txGroupId was sometimes incorrectedly checked for approval-less tx types.
This commit is contained in:
parent
16c1b13ab2
commit
c80ac9e321
33
src/main/java/org/qora/api/model/AggregatedOrder.java
Normal file
33
src/main/java/org/qora/api/model/AggregatedOrder.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package org.qora.api.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
|
||||||
|
import org.qora.data.asset.OrderData;
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.NONE)
|
||||||
|
public class AggregatedOrder {
|
||||||
|
|
||||||
|
private OrderData orderData;
|
||||||
|
|
||||||
|
protected AggregatedOrder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AggregatedOrder(OrderData orderData) {
|
||||||
|
this.orderData = orderData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "price")
|
||||||
|
public BigDecimal getPrice() {
|
||||||
|
return this.orderData.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "unfulfilled")
|
||||||
|
public BigDecimal getUnfulfilled() {
|
||||||
|
return this.orderData.getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
package org.qora.api.model;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
|
||||||
|
|
||||||
import org.qora.data.asset.OrderData;
|
|
||||||
import org.qora.data.asset.TradeData;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(description = "Asset order info, maybe including trades")
|
|
||||||
// All properties to be converted to JSON via JAX-RS
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
public class OrderWithTrades {
|
|
||||||
|
|
||||||
@Schema(implementation = OrderData.class, name = "order", title = "order data")
|
|
||||||
@XmlElement(name = "order")
|
|
||||||
public OrderData orderData;
|
|
||||||
|
|
||||||
List<TradeData> trades;
|
|
||||||
|
|
||||||
// For JAX-RS
|
|
||||||
protected OrderWithTrades() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrderWithTrades(OrderData orderData, List<TradeData> trades) {
|
|
||||||
this.orderData = orderData;
|
|
||||||
this.trades = trades;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
@ -28,6 +29,7 @@ import org.qora.api.ApiError;
|
|||||||
import org.qora.api.ApiErrors;
|
import org.qora.api.ApiErrors;
|
||||||
import org.qora.api.ApiException;
|
import org.qora.api.ApiException;
|
||||||
import org.qora.api.ApiExceptionFactory;
|
import org.qora.api.ApiExceptionFactory;
|
||||||
|
import org.qora.api.model.AggregatedOrder;
|
||||||
import org.qora.api.model.TradeWithOrderInfo;
|
import org.qora.api.model.TradeWithOrderInfo;
|
||||||
import org.qora.api.resource.TransactionsResource.ConfirmationStatus;
|
import org.qora.api.resource.TransactionsResource.ConfirmationStatus;
|
||||||
import org.qora.crypto.Crypto;
|
import org.qora.crypto.Crypto;
|
||||||
@ -175,9 +177,9 @@ public class AssetsResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/orderbook/{assetid}/{otherassetid}")
|
@Path("/openorders/{assetid}/{otherassetid}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Asset order book",
|
summary = "Detailed asset open order book",
|
||||||
description = "Returns open orders, offering {assetid} for {otherassetid} in return.",
|
description = "Returns open orders, offering {assetid} for {otherassetid} in return.",
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@ -195,7 +197,7 @@ public class AssetsResource {
|
|||||||
@ApiErrors({
|
@ApiErrors({
|
||||||
ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE
|
ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE
|
||||||
})
|
})
|
||||||
public List<OrderData> getAssetOrders(@Parameter(
|
public List<OrderData> getOpenOrders(@Parameter(
|
||||||
ref = "assetid"
|
ref = "assetid"
|
||||||
) @PathParam("assetid") int assetId, @Parameter(
|
) @PathParam("assetid") int assetId, @Parameter(
|
||||||
ref = "otherassetid"
|
ref = "otherassetid"
|
||||||
@ -219,6 +221,54 @@ public class AssetsResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/orderbook/{assetid}/{otherassetid}")
|
||||||
|
@Operation(
|
||||||
|
summary = "Aggregated asset open order book",
|
||||||
|
description = "Returns open orders, offering {assetid} for {otherassetid} in return.",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "asset orders",
|
||||||
|
content = @Content(
|
||||||
|
array = @ArraySchema(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = AggregatedOrder.class
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiErrors({
|
||||||
|
ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE
|
||||||
|
})
|
||||||
|
public List<AggregatedOrder> getAggregatedOpenOrders(@Parameter(
|
||||||
|
ref = "assetid"
|
||||||
|
) @PathParam("assetid") int assetId, @Parameter(
|
||||||
|
ref = "otherassetid"
|
||||||
|
) @PathParam("otherassetid") int otherAssetId, @Parameter(
|
||||||
|
ref = "limit"
|
||||||
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
|
ref = "offset"
|
||||||
|
) @QueryParam("offset") Integer offset, @Parameter(
|
||||||
|
ref = "reverse"
|
||||||
|
) @QueryParam("reverse") Boolean reverse) {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
if (!repository.getAssetRepository().assetExists(assetId))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
|
if (!repository.getAssetRepository().assetExists(otherAssetId))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
|
List<OrderData> orders = repository.getAssetRepository().getAggregatedOpenOrders(assetId, otherAssetId, limit, offset, reverse);
|
||||||
|
|
||||||
|
// Map to aggregated form
|
||||||
|
return orders.stream().map(orderData -> new AggregatedOrder(orderData)).collect(Collectors.toList());
|
||||||
|
} catch (DataException e) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/trades/{assetid}/{otherassetid}")
|
@Path("/trades/{assetid}/{otherassetid}")
|
||||||
@Operation(
|
@Operation(
|
||||||
@ -382,7 +432,7 @@ public class AssetsResource {
|
|||||||
@ApiErrors({
|
@ApiErrors({
|
||||||
ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE
|
ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE
|
||||||
})
|
})
|
||||||
public List<AccountBalanceData> getAssets(@PathParam("address") String address, @Parameter(
|
public List<AccountBalanceData> getOwnedAssets(@PathParam("address") String address, @Parameter(
|
||||||
ref = "limit"
|
ref = "limit"
|
||||||
) @QueryParam("limit") Integer limit, @Parameter(
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
ref = "offset"
|
ref = "offset"
|
||||||
@ -456,7 +506,7 @@ public class AssetsResource {
|
|||||||
@ApiErrors({
|
@ApiErrors({
|
||||||
ApiError.INVALID_ADDRESS, ApiError.ADDRESS_NO_EXISTS, ApiError.REPOSITORY_ISSUE
|
ApiError.INVALID_ADDRESS, ApiError.ADDRESS_NO_EXISTS, ApiError.REPOSITORY_ISSUE
|
||||||
})
|
})
|
||||||
public List<OrderData> getAssetOrders(@PathParam("address") String address, @QueryParam("includeClosed") boolean includeClosed,
|
public List<OrderData> getAccountOrders(@PathParam("address") String address, @QueryParam("includeClosed") boolean includeClosed,
|
||||||
@QueryParam("includeFulfilled") boolean includeFulfilled, @Parameter(
|
@QueryParam("includeFulfilled") boolean includeFulfilled, @Parameter(
|
||||||
ref = "limit"
|
ref = "limit"
|
||||||
) @QueryParam("limit") Integer limit, @Parameter(
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
@ -485,6 +535,61 @@ public class AssetsResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/orders/{address}/{assetid}/{otherassetid}")
|
||||||
|
@Operation(
|
||||||
|
summary = "Asset orders created by this address, limited to one specific asset pair",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "Asset orders",
|
||||||
|
content = @Content(
|
||||||
|
array = @ArraySchema(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = OrderData.class
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiErrors({
|
||||||
|
ApiError.INVALID_ADDRESS, ApiError.ADDRESS_NO_EXISTS, ApiError.REPOSITORY_ISSUE
|
||||||
|
})
|
||||||
|
public List<OrderData> getAccountAssetPairOrders(@PathParam("address") String address, @PathParam("assetid") int assetId, @PathParam("otherassetid") int otherAssetId, @QueryParam("includeClosed") boolean includeClosed,
|
||||||
|
@QueryParam("includeFulfilled") boolean includeFulfilled, @Parameter(
|
||||||
|
ref = "limit"
|
||||||
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
|
ref = "offset"
|
||||||
|
) @QueryParam("offset") Integer offset, @Parameter(
|
||||||
|
ref = "reverse"
|
||||||
|
) @QueryParam("reverse") Boolean reverse) {
|
||||||
|
if (!Crypto.isValidAddress(address))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
AccountData accountData = repository.getAccountRepository().getAccount(address);
|
||||||
|
|
||||||
|
if (accountData == null)
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_NO_EXISTS);
|
||||||
|
|
||||||
|
byte[] publicKey = accountData.getPublicKey();
|
||||||
|
if (publicKey == null)
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_NO_EXISTS);
|
||||||
|
|
||||||
|
if (!repository.getAssetRepository().assetExists(assetId))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
|
if (!repository.getAssetRepository().assetExists(otherAssetId))
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||||
|
|
||||||
|
return repository.getAssetRepository().getAccountsOrders(publicKey, assetId, otherAssetId, includeClosed, includeFulfilled, limit, offset, reverse);
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (DataException e) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/transactions/{assetid}")
|
@Path("/transactions/{assetid}")
|
||||||
@Operation(
|
@Operation(
|
||||||
|
@ -701,6 +701,40 @@ public class Block {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether Block's timestamp is valid.
|
||||||
|
* <p>
|
||||||
|
* Used by BlockGenerator to check whether it's time to forge new block,
|
||||||
|
* and also used by Block.isValid for checks (if not testnet).
|
||||||
|
*
|
||||||
|
* @return ValidationResult.OK if timestamp valid, or some other ValidationResult otherwise.
|
||||||
|
* @throws DataException
|
||||||
|
*/
|
||||||
|
public ValidationResult isTimestampValid() throws DataException {
|
||||||
|
BlockData parentBlockData = this.repository.getBlockRepository().fromSignature(this.blockData.getReference());
|
||||||
|
if (parentBlockData == null)
|
||||||
|
return ValidationResult.PARENT_DOES_NOT_EXIST;
|
||||||
|
|
||||||
|
// Check timestamp is newer than parent timestamp
|
||||||
|
if (this.blockData.getTimestamp() <= parentBlockData.getTimestamp())
|
||||||
|
return ValidationResult.TIMESTAMP_OLDER_THAN_PARENT;
|
||||||
|
|
||||||
|
// Check timestamp is not in the future (within configurable ~500ms margin)
|
||||||
|
if (this.blockData.getTimestamp() - BlockChain.getInstance().getBlockTimestampMargin() > NTP.getTime())
|
||||||
|
return ValidationResult.TIMESTAMP_IN_FUTURE;
|
||||||
|
|
||||||
|
// Legacy gen1 test: check timestamp milliseconds is the same as parent timestamp milliseconds?
|
||||||
|
if (this.blockData.getTimestamp() % 1000 != parentBlockData.getTimestamp() % 1000)
|
||||||
|
return ValidationResult.TIMESTAMP_MS_INCORRECT;
|
||||||
|
|
||||||
|
// Too early to forge block?
|
||||||
|
// XXX DISABLED as it doesn't work - but why?
|
||||||
|
// if (this.blockData.getTimestamp() < parentBlock.getBlockData().getTimestamp() + BlockChain.getInstance().getMinBlockTime())
|
||||||
|
// return ValidationResult.TIMESTAMP_TOO_SOON;
|
||||||
|
|
||||||
|
return ValidationResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether Block is valid.
|
* Returns whether Block is valid.
|
||||||
* <p>
|
* <p>
|
||||||
@ -733,18 +767,10 @@ public class Block {
|
|||||||
|
|
||||||
// These checks are disabled for testnet
|
// These checks are disabled for testnet
|
||||||
if (!BlockChain.getInstance().isTestNet()) {
|
if (!BlockChain.getInstance().isTestNet()) {
|
||||||
// Check timestamp is not in the future (within configurable ~500ms margin)
|
ValidationResult timestampResult = this.isTimestampValid();
|
||||||
if (this.blockData.getTimestamp() - BlockChain.getInstance().getBlockTimestampMargin() > NTP.getTime())
|
|
||||||
return ValidationResult.TIMESTAMP_IN_FUTURE;
|
|
||||||
|
|
||||||
// Legacy gen1 test: check timestamp milliseconds is the same as parent timestamp milliseconds?
|
if (timestampResult != ValidationResult.OK)
|
||||||
if (this.blockData.getTimestamp() % 1000 != parentBlockData.getTimestamp() % 1000)
|
return timestampResult;
|
||||||
return ValidationResult.TIMESTAMP_MS_INCORRECT;
|
|
||||||
|
|
||||||
// Too early to forge block?
|
|
||||||
// XXX DISABLED as it doesn't work - but why?
|
|
||||||
// if (this.blockData.getTimestamp() < parentBlock.getBlockData().getTimestamp() + BlockChain.getInstance().getMinBlockTime())
|
|
||||||
// return ValidationResult.TIMESTAMP_TOO_SOON;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check block version
|
// Check block version
|
||||||
|
@ -81,6 +81,11 @@ public class BlockGenerator extends Thread {
|
|||||||
Lock blockchainLock = Controller.getInstance().getBlockchainLock();
|
Lock blockchainLock = Controller.getInstance().getBlockchainLock();
|
||||||
if (blockchainLock.tryLock())
|
if (blockchainLock.tryLock())
|
||||||
generation: try {
|
generation: try {
|
||||||
|
// Is new block's timestamp valid yet?
|
||||||
|
// We do a separate check as some timestamp checks are skipped for testnet
|
||||||
|
if (newBlock.isTimestampValid() != ValidationResult.OK)
|
||||||
|
break generation;
|
||||||
|
|
||||||
// Is new block valid yet? (Before adding unconfirmed transactions)
|
// Is new block valid yet? (Before adding unconfirmed transactions)
|
||||||
if (newBlock.isValid() != ValidationResult.OK)
|
if (newBlock.isValid() != ValidationResult.OK)
|
||||||
break generation;
|
break generation;
|
||||||
|
@ -40,9 +40,14 @@ public interface AssetRepository {
|
|||||||
return getOpenOrders(haveAssetId, wantAssetId, null, null, null);
|
return getOpenOrders(haveAssetId, wantAssetId, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OrderData> getAggregatedOpenOrders(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||||
|
|
||||||
public List<OrderData> getAccountsOrders(byte[] publicKey, boolean includeClosed, boolean includeFulfilled, Integer limit, Integer offset, Boolean reverse)
|
public List<OrderData> getAccountsOrders(byte[] publicKey, boolean includeClosed, boolean includeFulfilled, Integer limit, Integer offset, Boolean reverse)
|
||||||
throws DataException;
|
throws DataException;
|
||||||
|
|
||||||
|
public List<OrderData> getAccountsOrders(byte[] publicKey, long haveAssetId, long wantAssetId, boolean includeClosed, boolean includeFulfilled,
|
||||||
|
Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||||
|
|
||||||
public default List<OrderData> getAccountsOrders(byte[] publicKey, boolean includeClosed, boolean includeFulfilled) throws DataException {
|
public default List<OrderData> getAccountsOrders(byte[] publicKey, boolean includeClosed, boolean includeFulfilled) throws DataException {
|
||||||
return getAccountsOrders(publicKey, includeClosed, includeFulfilled, null, null, null);
|
return getAccountsOrders(publicKey, includeClosed, includeFulfilled, null, null, null);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,36 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
return orders;
|
return orders;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch asset orders from repository", e);
|
throw new DataException("Unable to fetch open asset orders from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OrderData> 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";
|
||||||
|
if (reverse != null && reverse)
|
||||||
|
sql += " DESC";
|
||||||
|
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
|
||||||
|
|
||||||
|
List<OrderData> orders = new ArrayList<OrderData>();
|
||||||
|
|
||||||
|
try (ResultSet resultSet = this.repository.checkedExecute(sql, haveAssetId, wantAssetId)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return orders;
|
||||||
|
|
||||||
|
do {
|
||||||
|
BigDecimal price = resultSet.getBigDecimal(1);
|
||||||
|
BigDecimal totalUnfulfilled = resultSet.getBigDecimal(2);
|
||||||
|
long timestamp = resultSet.getTimestamp(3).getTime();
|
||||||
|
|
||||||
|
OrderData order = new OrderData(null, null, haveAssetId, wantAssetId, totalUnfulfilled, BigDecimal.ZERO, price, timestamp, false, false);
|
||||||
|
orders.add(order);
|
||||||
|
} while (resultSet.next());
|
||||||
|
|
||||||
|
return orders;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch aggregated open asset orders from repository", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +280,44 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OrderData> getAccountsOrders(byte[] publicKey, long haveAssetId, long wantAssetId, boolean includeClosed, boolean includeFulfilled, Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||||
|
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 = ?";
|
||||||
|
if (!includeClosed)
|
||||||
|
sql += " AND is_closed = FALSE";
|
||||||
|
if (!includeFulfilled)
|
||||||
|
sql += " AND is_fulfilled = FALSE";
|
||||||
|
sql += " ORDER BY ordered";
|
||||||
|
if (reverse != null && reverse)
|
||||||
|
sql += " DESC";
|
||||||
|
sql += HSQLDBRepository.limitOffsetSql(limit, offset);
|
||||||
|
|
||||||
|
List<OrderData> orders = new ArrayList<OrderData>();
|
||||||
|
|
||||||
|
try (ResultSet resultSet = this.repository.checkedExecute(sql, publicKey, haveAssetId, wantAssetId)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return orders;
|
||||||
|
|
||||||
|
do {
|
||||||
|
byte[] orderId = resultSet.getBytes(1);
|
||||||
|
BigDecimal amount = resultSet.getBigDecimal(2);
|
||||||
|
BigDecimal fulfilled = resultSet.getBigDecimal(3);
|
||||||
|
BigDecimal price = resultSet.getBigDecimal(4);
|
||||||
|
long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime();
|
||||||
|
boolean isClosed = resultSet.getBoolean(6);
|
||||||
|
boolean isFulfilled = resultSet.getBoolean(7);
|
||||||
|
|
||||||
|
OrderData order = new OrderData(orderId, publicKey, haveAssetId, wantAssetId, amount, fulfilled, price, timestamp, isClosed,
|
||||||
|
isFulfilled);
|
||||||
|
orders.add(order);
|
||||||
|
} while (resultSet.next());
|
||||||
|
|
||||||
|
return orders;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch account's asset orders from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@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");
|
||||||
|
@ -500,8 +500,8 @@ public abstract class Transaction {
|
|||||||
int txGroupId = this.transactionData.getTxGroupId();
|
int txGroupId = this.transactionData.getTxGroupId();
|
||||||
|
|
||||||
// If transaction type doesn't need approval then we insist on NO_GROUP
|
// If transaction type doesn't need approval then we insist on NO_GROUP
|
||||||
if (!this.transactionData.getType().needsApproval && txGroupId != Group.NO_GROUP)
|
if (!this.transactionData.getType().needsApproval)
|
||||||
return false;
|
return txGroupId == Group.NO_GROUP;
|
||||||
|
|
||||||
// Handling NO_GROUP
|
// Handling NO_GROUP
|
||||||
if (txGroupId == Group.NO_GROUP)
|
if (txGroupId == Group.NO_GROUP)
|
||||||
|
Loading…
Reference in New Issue
Block a user