mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-12 02:05:50 +00:00
Added API call for creating an asset bid/ask order
Also added test for missing creator public key on API-submitted transaction creation calls, like /payments/pay or /asset/issue. (Needs to be an OpenAPI validator added at some point).
This commit is contained in:
parent
17f3958ad6
commit
2497ac256c
2
pom.xml
2
pom.xml
@ -4,7 +4,7 @@
|
|||||||
<groupId>org.qora</groupId>
|
<groupId>org.qora</groupId>
|
||||||
<artifactId>qora-core</artifactId>
|
<artifactId>qora-core</artifactId>
|
||||||
<version>2.0.0-SNAPSHOT</version>
|
<version>2.0.0-SNAPSHOT</version>
|
||||||
<packaging>bundle</packaging>
|
<packaging>jar</packaging>
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<bouncycastle.version>1.60</bouncycastle.version>
|
<bouncycastle.version>1.60</bouncycastle.version>
|
||||||
|
@ -32,6 +32,7 @@ import org.qora.data.account.AccountBalanceData;
|
|||||||
import org.qora.data.asset.AssetData;
|
import org.qora.data.asset.AssetData;
|
||||||
import org.qora.data.asset.OrderData;
|
import org.qora.data.asset.OrderData;
|
||||||
import org.qora.data.asset.TradeData;
|
import org.qora.data.asset.TradeData;
|
||||||
|
import org.qora.data.transaction.CreateOrderTransactionData;
|
||||||
import org.qora.data.transaction.IssueAssetTransactionData;
|
import org.qora.data.transaction.IssueAssetTransactionData;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.Repository;
|
import org.qora.repository.Repository;
|
||||||
@ -39,6 +40,7 @@ import org.qora.repository.RepositoryManager;
|
|||||||
import org.qora.transaction.Transaction;
|
import org.qora.transaction.Transaction;
|
||||||
import org.qora.transaction.Transaction.ValidationResult;
|
import org.qora.transaction.Transaction.ValidationResult;
|
||||||
import org.qora.transform.TransformationException;
|
import org.qora.transform.TransformationException;
|
||||||
|
import org.qora.transform.transaction.CreateOrderTransactionTransformer;
|
||||||
import org.qora.transform.transaction.IssueAssetTransactionTransformer;
|
import org.qora.transform.transaction.IssueAssetTransactionTransformer;
|
||||||
import org.qora.utils.Base58;
|
import org.qora.utils.Base58;
|
||||||
|
|
||||||
@ -243,7 +245,7 @@ public class AssetsResource {
|
|||||||
),
|
),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "raw, unsigned payment transaction encoded in Base58",
|
description = "raw, unsigned, ISSUE_ASSET transaction encoded in Base58",
|
||||||
content = @Content(
|
content = @Content(
|
||||||
mediaType = MediaType.TEXT_PLAIN,
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
schema = @Schema(
|
schema = @Schema(
|
||||||
@ -271,4 +273,45 @@ public class AssetsResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/order")
|
||||||
|
@Operation(
|
||||||
|
summary = "Create asset order",
|
||||||
|
requestBody = @RequestBody(
|
||||||
|
required = true,
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON,
|
||||||
|
schema = @Schema(implementation = CreateOrderTransactionData.class)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "raw, unsigned, CREATE_ORDER transaction encoded in Base58",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiErrors({ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE, ApiError.TRANSACTION_INVALID})
|
||||||
|
public String createOrder(CreateOrderTransactionData transactionData) {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||||
|
|
||||||
|
ValidationResult result = transaction.isValidUnconfirmed();
|
||||||
|
if (result != ValidationResult.OK)
|
||||||
|
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||||
|
|
||||||
|
byte[] bytes = CreateOrderTransactionTransformer.toBytes(transactionData);
|
||||||
|
return Base58.encode(bytes);
|
||||||
|
} catch (TransformationException e) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||||
|
} catch (DataException e) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ public class NamesResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("/register")
|
@Path("/register")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Build raw, unsigned REGISTER_NAME transaction",
|
summary = "Build raw, unsigned, REGISTER_NAME transaction",
|
||||||
requestBody = @RequestBody(
|
requestBody = @RequestBody(
|
||||||
required = true,
|
required = true,
|
||||||
content = @Content(
|
content = @Content(
|
||||||
@ -50,7 +50,7 @@ public class NamesResource {
|
|||||||
),
|
),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "raw, unsigned REGISTER_NAME transaction encoded in Base58",
|
description = "raw, unsigned, REGISTER_NAME transaction encoded in Base58",
|
||||||
content = @Content(
|
content = @Content(
|
||||||
mediaType = MediaType.TEXT_PLAIN,
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
schema = @Schema(
|
schema = @Schema(
|
||||||
|
@ -38,7 +38,7 @@ public class PaymentsResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("/pay")
|
@Path("/pay")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Build raw, unsigned payment transaction",
|
summary = "Build raw, unsigned, PAYMENT transaction",
|
||||||
requestBody = @RequestBody(
|
requestBody = @RequestBody(
|
||||||
required = true,
|
required = true,
|
||||||
content = @Content(
|
content = @Content(
|
||||||
@ -50,7 +50,7 @@ public class PaymentsResource {
|
|||||||
),
|
),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "raw, unsigned payment transaction encoded in Base58",
|
description = "raw, unsigned, PAYMENT transaction encoded in Base58",
|
||||||
content = @Content(
|
content = @Content(
|
||||||
mediaType = MediaType.TEXT_PLAIN,
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
schema = @Schema(
|
schema = @Schema(
|
||||||
|
@ -4,10 +4,12 @@ import java.math.BigDecimal;
|
|||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
|
||||||
import org.qora.transaction.Transaction.TransactionType;
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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 JAX-RS
|
// All properties to be converted to JSON via JAX-RS
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
@ -15,15 +17,20 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
public class CreateOrderTransactionData extends TransactionData {
|
public class CreateOrderTransactionData extends TransactionData {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
@Schema(description = "asset on offer to give by order creator")
|
||||||
private long haveAssetId;
|
private long haveAssetId;
|
||||||
|
@Schema(description = "asset wanted to receive by order creator")
|
||||||
private long wantAssetId;
|
private long wantAssetId;
|
||||||
|
@Schema(description = "amount of \"have\" asset to trade")
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
@Schema(description = "amount of \"want\" asset to receive per unit of \"have\" asset traded")
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
// For JAX-RS
|
// For JAX-RS
|
||||||
protected CreateOrderTransactionData() {
|
protected CreateOrderTransactionData() {
|
||||||
|
super(TransactionType.CREATE_ASSET_ORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
public CreateOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
||||||
@ -59,4 +66,17 @@ public class CreateOrderTransactionData extends TransactionData {
|
|||||||
return this.price;
|
return this.price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-expose creatorPublicKey for this transaction type for JAXB
|
||||||
|
@XmlElement(name = "creatorPublicKey")
|
||||||
|
@Schema(name = "creatorPublicKey", description = "order creator's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||||
|
public byte[] getOrderCreatorPublicKey() {
|
||||||
|
return this.creatorPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "creatorPublicKey")
|
||||||
|
@Schema(name = "creatorPublicKey", description = "order creator's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||||
|
public void setOrderCreatorPublicKey(byte[] creatorPublicKey) {
|
||||||
|
this.creatorPublicKey = creatorPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ public abstract class Transaction {
|
|||||||
AT_IS_FINISHED(40),
|
AT_IS_FINISHED(40),
|
||||||
ASSET_DOES_NOT_MATCH_AT(41),
|
ASSET_DOES_NOT_MATCH_AT(41),
|
||||||
ASSET_ALREADY_EXISTS(43),
|
ASSET_ALREADY_EXISTS(43),
|
||||||
|
MISSING_CREATOR(44),
|
||||||
NOT_YET_RELEASED(1000);
|
NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
@ -364,6 +365,9 @@ public abstract class Transaction {
|
|||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
protected Account getCreator() throws DataException {
|
protected Account getCreator() throws DataException {
|
||||||
|
if (this.transactionData.getCreatorPublicKey() == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
return new PublicKeyAccount(this.repository, this.transactionData.getCreatorPublicKey());
|
return new PublicKeyAccount(this.repository, this.transactionData.getCreatorPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +436,9 @@ public abstract class Transaction {
|
|||||||
public ValidationResult isValidUnconfirmed() throws DataException {
|
public ValidationResult isValidUnconfirmed() throws DataException {
|
||||||
try {
|
try {
|
||||||
Account creator = this.getCreator();
|
Account creator = this.getCreator();
|
||||||
|
if (creator == null)
|
||||||
|
return ValidationResult.MISSING_CREATOR;
|
||||||
|
|
||||||
creator.setLastReference(creator.getUnconfirmedLastReference());
|
creator.setLastReference(creator.getUnconfirmedLastReference());
|
||||||
return this.isValid();
|
return this.isValid();
|
||||||
} finally {
|
} finally {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user