From 34382b6e69b5587caa42e7d3f14791e75b50e79b Mon Sep 17 00:00:00 2001 From: Phillip Lang Martinez Date: Tue, 22 Aug 2023 19:58:24 -0500 Subject: [PATCH 1/4] add endpoint --- .../org/qortal/api/resource/AtResource.java | 36 +++++++++++ .../data/transaction/CreationRequest.java | 63 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/main/java/org/qortal/data/transaction/CreationRequest.java diff --git a/src/main/java/org/qortal/api/resource/AtResource.java b/src/main/java/org/qortal/api/resource/AtResource.java index 29a2344d..4a5fbbdb 100644 --- a/src/main/java/org/qortal/api/resource/AtResource.java +++ b/src/main/java/org/qortal/api/resource/AtResource.java @@ -27,6 +27,7 @@ import org.qortal.api.ApiException; import org.qortal.api.ApiExceptionFactory; import org.qortal.data.at.ATData; import org.qortal.data.at.ATStateData; +import org.qortal.data.transaction.CreationRequest; import org.qortal.data.transaction.DeployAtTransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; @@ -156,6 +157,41 @@ public class AtResource { } } + @POST + @Path("/createMachineState") + @Operation( + summary = "Create MachineState bytes from the provided parameters", + requestBody = @RequestBody( + required = true, + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = CreationRequest.class + ) + ) + ), + responses = { + @ApiResponse( + description = "MachineState bytes", + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "string" + ) + ) + ) + } + ) + public byte[] createMachineState(CreationRequest request) { + return MachineState.toCreationBytes( + request.getCiyamAtVersion(), + request.getCodeBytes(), + request.getDataBytes(), + request.getNumCallStackPages(), + request.getNumUserStackPages(), + request.getMinActivationAmount() + ); + } @POST @Operation( summary = "Build raw, unsigned, DEPLOY_AT transaction", diff --git a/src/main/java/org/qortal/data/transaction/CreationRequest.java b/src/main/java/org/qortal/data/transaction/CreationRequest.java new file mode 100644 index 00000000..e5b6ffdb --- /dev/null +++ b/src/main/java/org/qortal/data/transaction/CreationRequest.java @@ -0,0 +1,63 @@ +package org.qortal.data.transaction; + +public class CreationRequest { + + private short ciyamAtVersion; + private byte[] codeBytes; + private byte[] dataBytes; + private short numCallStackPages; + private short numUserStackPages; + private long minActivationAmount; + + // Default constructor for JSON deserialization + public CreationRequest() {} + + // Getters and setters + public short getCiyamAtVersion() { + return ciyamAtVersion; + } + + public void setCiyamAtVersion(short ciyamAtVersion) { + this.ciyamAtVersion = ciyamAtVersion; + } + + public byte[] getCodeBytes() { + return codeBytes; + } + + public void setCodeBytes(byte[] codeBytes) { + this.codeBytes = codeBytes; + } + + public byte[] getDataBytes() { + return dataBytes; + } + + public void setDataBytes(byte[] dataBytes) { + this.dataBytes = dataBytes; + } + + public short getNumCallStackPages() { + return numCallStackPages; + } + + public void setNumCallStackPages(short numCallStackPages) { + this.numCallStackPages = numCallStackPages; + } + + public short getNumUserStackPages() { + return numUserStackPages; + } + + public void setNumUserStackPages(short numUserStackPages) { + this.numUserStackPages = numUserStackPages; + } + + public long getMinActivationAmount() { + return minActivationAmount; + } + + public void setMinActivationAmount(long minActivationAmount) { + this.minActivationAmount = minActivationAmount; + } +} \ No newline at end of file From 24ff3ab58174e3f31232f6ddf0fee60d9ce7c9a5 Mon Sep 17 00:00:00 2001 From: Phillip Lang Martinez Date: Tue, 22 Aug 2023 22:46:58 -0500 Subject: [PATCH 2/4] base64 to byte array --- .../org/qortal/api/resource/AtResource.java | 1 + .../data/transaction/CreationRequest.java | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/AtResource.java b/src/main/java/org/qortal/api/resource/AtResource.java index 4a5fbbdb..fd045b7d 100644 --- a/src/main/java/org/qortal/api/resource/AtResource.java +++ b/src/main/java/org/qortal/api/resource/AtResource.java @@ -38,6 +38,7 @@ import org.qortal.transaction.Transaction.ValidationResult; import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.DeployAtTransactionTransformer; import org.qortal.utils.Base58; +import java.util.Base64; @Path("/at") @Tag(name = "Automated Transactions") diff --git a/src/main/java/org/qortal/data/transaction/CreationRequest.java b/src/main/java/org/qortal/data/transaction/CreationRequest.java index e5b6ffdb..efdfc738 100644 --- a/src/main/java/org/qortal/data/transaction/CreationRequest.java +++ b/src/main/java/org/qortal/data/transaction/CreationRequest.java @@ -1,10 +1,12 @@ package org.qortal.data.transaction; -public class CreationRequest { +import java.util.Base64; + +public class CreationRequest { private short ciyamAtVersion; - private byte[] codeBytes; - private byte[] dataBytes; + private String codeBytesBase64; + private String dataBytesBase64; private short numCallStackPages; private short numUserStackPages; private long minActivationAmount; @@ -22,19 +24,19 @@ public class CreationRequest { } public byte[] getCodeBytes() { - return codeBytes; + return Base64.getDecoder().decode(this.codeBytesBase64); } - public void setCodeBytes(byte[] codeBytes) { - this.codeBytes = codeBytes; + public void setCodeBytesBase64(String codeBytesBase64) { + this.codeBytesBase64 = codeBytesBase64; } public byte[] getDataBytes() { - return dataBytes; + return Base64.getDecoder().decode(this.dataBytesBase64); } - public void setDataBytes(byte[] dataBytes) { - this.dataBytes = dataBytes; + public void setDataBytesBase64(String dataBytesBase64) { + this.dataBytesBase64 = dataBytesBase64; } public short getNumCallStackPages() { @@ -60,4 +62,4 @@ public class CreationRequest { public void setMinActivationAmount(long minActivationAmount) { this.minActivationAmount = minActivationAmount; } -} \ No newline at end of file +} From b051f9be89422391deaaf85aa7666d5bbc766070 Mon Sep 17 00:00:00 2001 From: Phillip Lang Martinez Date: Wed, 23 Aug 2023 14:41:50 -0500 Subject: [PATCH 3/4] fix strings in request --- .../org/qortal/api/resource/AtResource.java | 36 +++++++++++++------ .../data/transaction/CreationRequest.java | 19 ++++++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/AtResource.java b/src/main/java/org/qortal/api/resource/AtResource.java index fd045b7d..80a6e299 100644 --- a/src/main/java/org/qortal/api/resource/AtResource.java +++ b/src/main/java/org/qortal/api/resource/AtResource.java @@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -39,10 +40,13 @@ import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.DeployAtTransactionTransformer; import org.qortal.utils.Base58; import java.util.Base64; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; @Path("/at") @Tag(name = "Automated Transactions") public class AtResource { + private static final Logger logger = LoggerFactory.getLogger(AtResource.class); @Context HttpServletRequest request; @@ -183,15 +187,27 @@ public class AtResource { ) } ) - public byte[] createMachineState(CreationRequest request) { - return MachineState.toCreationBytes( - request.getCiyamAtVersion(), - request.getCodeBytes(), - request.getDataBytes(), - request.getNumCallStackPages(), - request.getNumUserStackPages(), - request.getMinActivationAmount() - ); + public String createMachineState(String jsonBody) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + CreationRequest request = objectMapper.readValue(jsonBody, CreationRequest.class); + + logger.info("ciyamAtVersion: {}", request.getCiyamAtVersion()); + logger.info("codeBytes: {}", request.getCodeBytes()); + logger.info("codeBytes: {}", request.getNumUserStackPages()); + logger.info("codeBytes: {}", request.getDataBytes()); + logger.info("codeBytes: {}", request.getNumCallStackPages()); + logger.info("codeBytes: {}", request.getMinActivationAmount()); + byte[] creationBytes = MachineState.toCreationBytes( + request.getCiyamAtVersion(), + request.getCodeBytes(), + request.getDataBytes(), + request.getNumCallStackPages(), + request.getNumUserStackPages(), + request.getMinActivationAmount() + ); + return Base58.encode(creationBytes); + + } @POST @Operation( diff --git a/src/main/java/org/qortal/data/transaction/CreationRequest.java b/src/main/java/org/qortal/data/transaction/CreationRequest.java index efdfc738..9d50458c 100644 --- a/src/main/java/org/qortal/data/transaction/CreationRequest.java +++ b/src/main/java/org/qortal/data/transaction/CreationRequest.java @@ -1,10 +1,11 @@ package org.qortal.data.transaction; import java.util.Base64; - +import com.fasterxml.jackson.annotation.JsonProperty; public class CreationRequest { private short ciyamAtVersion; + @JsonProperty("codeBytesBase64") private String codeBytesBase64; private String dataBytesBase64; private short numCallStackPages; @@ -24,15 +25,27 @@ public class CreationRequest { } public byte[] getCodeBytes() { - return Base64.getDecoder().decode(this.codeBytesBase64); + if (this.codeBytesBase64 != null) { + return Base64.getDecoder().decode(this.codeBytesBase64); + } + return new byte[0]; } public void setCodeBytesBase64(String codeBytesBase64) { this.codeBytesBase64 = codeBytesBase64; } + public String getCodeBytes2() { + return codeBytesBase64; + + } + + public byte[] getDataBytes() { - return Base64.getDecoder().decode(this.dataBytesBase64); + if (this.dataBytesBase64 != null) { + return Base64.getDecoder().decode(this.dataBytesBase64); + } + return new byte[0]; } public void setDataBytesBase64(String dataBytesBase64) { From 3fbcc50503aa68b08301c1b5620c37a1ce7c33ae Mon Sep 17 00:00:00 2001 From: CalDescent Date: Fri, 25 Aug 2023 11:01:48 +0100 Subject: [PATCH 4/4] Fixed deserialization issues with CreationRequest, added validation, and made small code tweaks for consistency with other endpoints. --- .../qortal/api/model/AtCreationRequest.java | 102 ++++++++++++++++++ .../org/qortal/api/resource/AtResource.java | 55 +++++----- .../data/transaction/CreationRequest.java | 78 -------------- 3 files changed, 129 insertions(+), 106 deletions(-) create mode 100644 src/main/java/org/qortal/api/model/AtCreationRequest.java delete mode 100644 src/main/java/org/qortal/data/transaction/CreationRequest.java diff --git a/src/main/java/org/qortal/api/model/AtCreationRequest.java b/src/main/java/org/qortal/api/model/AtCreationRequest.java new file mode 100644 index 00000000..14ccdaa2 --- /dev/null +++ b/src/main/java/org/qortal/api/model/AtCreationRequest.java @@ -0,0 +1,102 @@ +package org.qortal.api.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlTransient; + +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.DecoderException; + +@XmlAccessorType(XmlAccessType.FIELD) +public class AtCreationRequest { + + @Schema(description = "CIYAM AT version", example = "2") + private short ciyamAtVersion; + + @Schema(description = "base64-encoded code bytes", example = "") + private String codeBytesBase64; + + @Schema(description = "base64-encoded data bytes", example = "") + private String dataBytesBase64; + + private short numCallStackPages; + private short numUserStackPages; + private long minActivationAmount; + + // Default constructor for JSON deserialization + public AtCreationRequest() {} + + // Getters and setters + public short getCiyamAtVersion() { + return ciyamAtVersion; + } + + public void setCiyamAtVersion(short ciyamAtVersion) { + this.ciyamAtVersion = ciyamAtVersion; + } + + + public String getCodeBytesBase64() { + return this.codeBytesBase64; + } + + @XmlTransient + @Schema(hidden = true) + public byte[] getCodeBytes() { + if (this.codeBytesBase64 != null) { + try { + return Base64.decode(this.codeBytesBase64); + } + catch (DecoderException e) { + return null; + } + } + return null; + } + + + public String getDataBytesBase64() { + return this.dataBytesBase64; + } + + @XmlTransient + @Schema(hidden = true) + public byte[] getDataBytes() { + if (this.dataBytesBase64 != null) { + try { + return Base64.decode(this.dataBytesBase64); + } + catch (DecoderException e) { + return null; + } + } + return null; + } + + + public short getNumCallStackPages() { + return numCallStackPages; + } + + public void setNumCallStackPages(short numCallStackPages) { + this.numCallStackPages = numCallStackPages; + } + + public short getNumUserStackPages() { + return numUserStackPages; + } + + public void setNumUserStackPages(short numUserStackPages) { + this.numUserStackPages = numUserStackPages; + } + + public long getMinActivationAmount() { + return minActivationAmount; + } + + public void setMinActivationAmount(long minActivationAmount) { + this.minActivationAmount = minActivationAmount; + } +} diff --git a/src/main/java/org/qortal/api/resource/AtResource.java b/src/main/java/org/qortal/api/resource/AtResource.java index 80a6e299..13bfec83 100644 --- a/src/main/java/org/qortal/api/resource/AtResource.java +++ b/src/main/java/org/qortal/api/resource/AtResource.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; -import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -28,7 +27,7 @@ import org.qortal.api.ApiException; import org.qortal.api.ApiExceptionFactory; import org.qortal.data.at.ATData; import org.qortal.data.at.ATStateData; -import org.qortal.data.transaction.CreationRequest; +import org.qortal.api.model.AtCreationRequest; import org.qortal.data.transaction.DeployAtTransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; @@ -39,10 +38,11 @@ import org.qortal.transaction.Transaction.ValidationResult; import org.qortal.transform.TransformationException; import org.qortal.transform.transaction.DeployAtTransactionTransformer; import org.qortal.utils.Base58; -import java.util.Base64; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; + + @Path("/at") @Tag(name = "Automated Transactions") public class AtResource { @@ -163,21 +163,21 @@ public class AtResource { } @POST - @Path("/createMachineState") + @Path("/create") @Operation( - summary = "Create MachineState bytes from the provided parameters", + summary = "Create base58-encoded AT creation bytes from the provided parameters", requestBody = @RequestBody( required = true, content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = CreationRequest.class + implementation = AtCreationRequest.class ) ) ), responses = { @ApiResponse( - description = "MachineState bytes", + description = "AT creation bytes suitable for use in a DEPLOY_AT transaction", content = @Content( mediaType = MediaType.TEXT_PLAIN, schema = @Schema( @@ -187,27 +187,26 @@ public class AtResource { ) } ) - public String createMachineState(String jsonBody) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - CreationRequest request = objectMapper.readValue(jsonBody, CreationRequest.class); - - logger.info("ciyamAtVersion: {}", request.getCiyamAtVersion()); - logger.info("codeBytes: {}", request.getCodeBytes()); - logger.info("codeBytes: {}", request.getNumUserStackPages()); - logger.info("codeBytes: {}", request.getDataBytes()); - logger.info("codeBytes: {}", request.getNumCallStackPages()); - logger.info("codeBytes: {}", request.getMinActivationAmount()); - byte[] creationBytes = MachineState.toCreationBytes( - request.getCiyamAtVersion(), - request.getCodeBytes(), - request.getDataBytes(), - request.getNumCallStackPages(), - request.getNumUserStackPages(), - request.getMinActivationAmount() - ); - return Base58.encode(creationBytes); - + public String create(AtCreationRequest atCreationRequest) { + if (atCreationRequest.getCiyamAtVersion() < 2) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "ciyamAtVersion must be at least 2"); + } + if (atCreationRequest.getCodeBytes() == null) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Valid codeBytesBase64 must be supplied"); + } + if (atCreationRequest.getDataBytes() == null) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Valid dataBytesBase64 must be supplied"); + } + byte[] creationBytes = MachineState.toCreationBytes( + atCreationRequest.getCiyamAtVersion(), + atCreationRequest.getCodeBytes(), + atCreationRequest.getDataBytes(), + atCreationRequest.getNumCallStackPages(), + atCreationRequest.getNumUserStackPages(), + atCreationRequest.getMinActivationAmount() + ); + return Base58.encode(creationBytes); } @POST @Operation( diff --git a/src/main/java/org/qortal/data/transaction/CreationRequest.java b/src/main/java/org/qortal/data/transaction/CreationRequest.java deleted file mode 100644 index 9d50458c..00000000 --- a/src/main/java/org/qortal/data/transaction/CreationRequest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.qortal.data.transaction; - -import java.util.Base64; -import com.fasterxml.jackson.annotation.JsonProperty; -public class CreationRequest { - - private short ciyamAtVersion; - @JsonProperty("codeBytesBase64") - private String codeBytesBase64; - private String dataBytesBase64; - private short numCallStackPages; - private short numUserStackPages; - private long minActivationAmount; - - // Default constructor for JSON deserialization - public CreationRequest() {} - - // Getters and setters - public short getCiyamAtVersion() { - return ciyamAtVersion; - } - - public void setCiyamAtVersion(short ciyamAtVersion) { - this.ciyamAtVersion = ciyamAtVersion; - } - - public byte[] getCodeBytes() { - if (this.codeBytesBase64 != null) { - return Base64.getDecoder().decode(this.codeBytesBase64); - } - return new byte[0]; - } - - public void setCodeBytesBase64(String codeBytesBase64) { - this.codeBytesBase64 = codeBytesBase64; - } - public String getCodeBytes2() { - return codeBytesBase64; - - } - - - - public byte[] getDataBytes() { - if (this.dataBytesBase64 != null) { - return Base64.getDecoder().decode(this.dataBytesBase64); - } - return new byte[0]; - } - - public void setDataBytesBase64(String dataBytesBase64) { - this.dataBytesBase64 = dataBytesBase64; - } - - public short getNumCallStackPages() { - return numCallStackPages; - } - - public void setNumCallStackPages(short numCallStackPages) { - this.numCallStackPages = numCallStackPages; - } - - public short getNumUserStackPages() { - return numUserStackPages; - } - - public void setNumUserStackPages(short numUserStackPages) { - this.numUserStackPages = numUserStackPages; - } - - public long getMinActivationAmount() { - return minActivationAmount; - } - - public void setMinActivationAmount(long minActivationAmount) { - this.minActivationAmount = minActivationAmount; - } -}