diff --git a/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java b/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java index 68fd11c..72350f4 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java @@ -11,6 +11,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import cy.agorise.graphenej.operations.CustomOperation; import org.bitcoinj.core.DumpedPrivateKey; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; @@ -343,7 +344,7 @@ public class Transaction implements ByteSerializable, JsonSerializable { } else if (operationId == OperationType.WORKER_CREATE_OPERATION.ordinal()) { //TODO: Add operation deserialization support } else if (operationId == OperationType.CUSTOM_OPERATION.ordinal()) { - //TODO: Add operation deserialization support + operation = context.deserialize(jsonOperation, CustomOperation.class); } else if (operationId == OperationType.ASSERT_OPERATION.ordinal()) { //TODO: Add operation deserialization support } else if (operationId == OperationType.BALANCE_CLAIM_OPERATION.ordinal()) { diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java b/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java index 2cef872..7d3e882 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java @@ -13,6 +13,8 @@ import cy.agorise.graphenej.models.ApiCall; import cy.agorise.graphenej.models.BaseResponse; import cy.agorise.graphenej.models.Block; import cy.agorise.graphenej.models.WitnessResponse; +import cy.agorise.graphenej.operations.CustomOperation; +import cy.agorise.graphenej.operations.LimitOrderCreateOperation; import cy.agorise.graphenej.operations.TransferOperation; import java.io.Serializable; @@ -88,6 +90,8 @@ public class GetBlock extends BaseGrapheneHandler { gson = new GsonBuilder() .registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()) .registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()) + .registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer()) + .registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer()) .registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()) .create(); WitnessResponse blockResponse = gson.fromJson(response, BlockResponse); diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java index bbb3cfb..1509be3 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java @@ -26,6 +26,7 @@ import cy.agorise.graphenej.models.DynamicGlobalProperties; import cy.agorise.graphenej.models.SubscriptionResponse; import cy.agorise.graphenej.models.WitnessResponse; import cy.agorise.graphenej.objects.Memo; +import cy.agorise.graphenej.operations.CustomOperation; import cy.agorise.graphenej.operations.LimitOrderCreateOperation; import cy.agorise.graphenej.operations.TransferOperation; @@ -89,6 +90,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()); builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()); builder.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer()); + builder.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer()); builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer()); builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); diff --git a/graphenej/src/main/java/cy/agorise/graphenej/operations/CustomOperation.java b/graphenej/src/main/java/cy/agorise/graphenej/operations/CustomOperation.java index de4d091..4678be0 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/operations/CustomOperation.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/operations/CustomOperation.java @@ -1,18 +1,17 @@ package cy.agorise.graphenej.operations; import com.google.common.primitives.Bytes; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import com.google.gson.*; import cy.agorise.graphenej.*; +import java.lang.reflect.Type; import java.util.LinkedList; import java.util.List; public class CustomOperation extends BaseOperation { private static final String KEY_FEE = "fee"; private static final String KEY_PAYER = "payer"; - private static final String KEY_REQUIRED_AUTH = "required_auths"; + private static final String KEY_REQUIRED_AUTHS = "required_auths"; private static final String KEY_ID = "id"; private static final String KEY_DATA = "data"; @@ -113,7 +112,7 @@ public class CustomOperation extends BaseOperation { requiredAuthArray.add(userAccount.getObjectId()); } } - jsonObject.add(KEY_REQUIRED_AUTH, requiredAuthArray); + jsonObject.add(KEY_REQUIRED_AUTHS, requiredAuthArray); jsonObject.addProperty(KEY_ID, operationId); jsonObject.addProperty(KEY_DATA, Util.bytesToHex(Util.hexlify(data))); @@ -121,4 +120,66 @@ public class CustomOperation extends BaseOperation { return array; } + + /** + * Deserializer used to convert the JSON-formatted representation of a custom_operation + * into its java object version. + * + * The following is an example of the serialized form of this operation: + * + * [ + * 35, + * { + * "fee": { + * "amount": 100000, + * "asset_id": "1.3.0" + * }, + * "payer": "1.2.20", + * "required_auths": [ + * "1.2.20" + * ], + * "id": 61166, + * "data": "736f6d652064617461" + * } + * ] + */ + public static class CustomOperationDeserializer implements JsonDeserializer { + + @Override + public CustomOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonArray()){ + // This block is used just to check if we are in the first step of the deserialization + // when we are dealing with an array. + JsonArray serializedCustomOperation = json.getAsJsonArray(); + if (serializedCustomOperation.get(0).getAsInt() != OperationType.CUSTOM_OPERATION.ordinal()){ + // If the operation type does not correspond to a custom operation, we return null + return null; + } else { + // Calling itself recursively, this is only done once, so there will be no problems. + return context.deserialize(serializedCustomOperation.get(1), CustomOperation.class); + } + }else{ + // This block is called in the second recursion and takes care of deserializing the + // limit order data itself. + JsonObject jsonObject = json.getAsJsonObject(); + + AssetAmount fee = context.deserialize(jsonObject.get(KEY_FEE), AssetAmount.class); + String payerId = jsonObject.get(KEY_PAYER) .getAsString(); + UserAccount payer = new UserAccount(payerId); + List requiredAuths = new LinkedList<>(); + JsonElement requiredAuthsElement = jsonObject.get(KEY_REQUIRED_AUTHS); + if ((requiredAuthsElement != null) && (requiredAuthsElement.isJsonArray())) { + JsonArray requiredAuthsArray = requiredAuthsElement.getAsJsonArray(); + for (JsonElement jsonElement : requiredAuthsArray) { + String userAccountId = jsonElement.getAsString(); + requiredAuths.add(new UserAccount(userAccountId)); + } + } + int operationId = jsonObject.get(KEY_ID).getAsInt(); + String data = new String(Util.hexToBytes(jsonObject.get(KEY_DATA).getAsString())); + + return new CustomOperation(fee, payer, operationId, requiredAuths, data); + } + } + } }