Merge pull request #2 from Carix/add_custom_operation
Custom operation support
This commit is contained in:
commit
0787b13483
9 changed files with 381 additions and 33 deletions
|
@ -11,6 +11,7 @@ import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.google.gson.JsonSerializationContext;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import org.bitcoinj.core.DumpedPrivateKey;
|
import org.bitcoinj.core.DumpedPrivateKey;
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
|
@ -343,7 +344,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
} else if (operationId == OperationType.WORKER_CREATE_OPERATION.ordinal()) {
|
} else if (operationId == OperationType.WORKER_CREATE_OPERATION.ordinal()) {
|
||||||
//TODO: Add operation deserialization support
|
//TODO: Add operation deserialization support
|
||||||
} else if (operationId == OperationType.CUSTOM_OPERATION.ordinal()) {
|
} 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()) {
|
} else if (operationId == OperationType.ASSERT_OPERATION.ordinal()) {
|
||||||
//TODO: Add operation deserialization support
|
//TODO: Add operation deserialization support
|
||||||
} else if (operationId == OperationType.BALANCE_CLAIM_OPERATION.ordinal()) {
|
} else if (operationId == OperationType.BALANCE_CLAIM_OPERATION.ordinal()) {
|
||||||
|
|
|
@ -26,6 +26,8 @@ import java.nio.ByteBuffer;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -92,6 +94,26 @@ public class Util {
|
||||||
return buffer.array();
|
return buffer.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes long value to a byte array.
|
||||||
|
* @param data Long value.
|
||||||
|
* @return Array of bytes.
|
||||||
|
*/
|
||||||
|
public static byte[] serializeLongToBytes(long data) {
|
||||||
|
List<Byte> bytes = new LinkedList<>();
|
||||||
|
long value = data;
|
||||||
|
do {
|
||||||
|
byte b = (byte)(value & 0x7F);
|
||||||
|
value >>= 7;
|
||||||
|
if (value != 0) {
|
||||||
|
b |= 0x80;
|
||||||
|
}
|
||||||
|
bytes.add(b);
|
||||||
|
} while (value != 0);
|
||||||
|
|
||||||
|
return Bytes.toArray(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function that compresses data using the LZMA algorithm.
|
* Utility function that compresses data using the LZMA algorithm.
|
||||||
* @param inputBytes Input bytes of the data to be compressed.
|
* @param inputBytes Input bytes of the data to be compressed.
|
||||||
|
|
|
@ -13,6 +13,8 @@ import cy.agorise.graphenej.models.ApiCall;
|
||||||
import cy.agorise.graphenej.models.BaseResponse;
|
import cy.agorise.graphenej.models.BaseResponse;
|
||||||
import cy.agorise.graphenej.models.Block;
|
import cy.agorise.graphenej.models.Block;
|
||||||
import cy.agorise.graphenej.models.WitnessResponse;
|
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 cy.agorise.graphenej.operations.TransferOperation;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -88,6 +90,8 @@ public class GetBlock extends BaseGrapheneHandler {
|
||||||
gson = new GsonBuilder()
|
gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||||
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||||
|
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||||
|
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||||
.create();
|
.create();
|
||||||
WitnessResponse<Block> blockResponse = gson.fromJson(response, BlockResponse);
|
WitnessResponse<Block> blockResponse = gson.fromJson(response, BlockResponse);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import cy.agorise.graphenej.models.DynamicGlobalProperties;
|
||||||
import cy.agorise.graphenej.models.SubscriptionResponse;
|
import cy.agorise.graphenej.models.SubscriptionResponse;
|
||||||
import cy.agorise.graphenej.models.WitnessResponse;
|
import cy.agorise.graphenej.models.WitnessResponse;
|
||||||
import cy.agorise.graphenej.objects.Memo;
|
import cy.agorise.graphenej.objects.Memo;
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
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(Transaction.class, new Transaction.TransactionDeserializer());
|
||||||
builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
|
builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
|
||||||
builder.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer());
|
builder.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer());
|
||||||
|
builder.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer());
|
||||||
builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
|
builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
|
||||||
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
|
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
|
||||||
builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
|
builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
package cy.agorise.graphenej.operations;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
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_AUTHS = "required_auths";
|
||||||
|
private static final String KEY_ID = "id";
|
||||||
|
private static final String KEY_DATA = "data";
|
||||||
|
|
||||||
|
private AssetAmount fee;
|
||||||
|
private UserAccount payer;
|
||||||
|
private List<UserAccount> requiredAuths;
|
||||||
|
private int operationId;
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
public CustomOperation(AssetAmount fee, UserAccount payer, int operationId, List<UserAccount> requiredAuths, String data) {
|
||||||
|
super(OperationType.CUSTOM_OPERATION);
|
||||||
|
this.fee = fee;
|
||||||
|
this.payer = payer;
|
||||||
|
this.operationId = operationId;
|
||||||
|
this.requiredAuths = new LinkedList<>();
|
||||||
|
if (requiredAuths != null) {
|
||||||
|
this.requiredAuths.addAll(requiredAuths);
|
||||||
|
}
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssetAmount getFee() {
|
||||||
|
return fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFee(AssetAmount fee) {
|
||||||
|
this.fee = fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount getPayer() {
|
||||||
|
return payer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayer(UserAccount payer) {
|
||||||
|
this.payer = payer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserAccount> getRequiredAuths() {
|
||||||
|
return requiredAuths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiredAuths(List<UserAccount> requiredAuths) {
|
||||||
|
this.requiredAuths = requiredAuths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOperationId() {
|
||||||
|
return operationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOperationId(int operationId) {
|
||||||
|
this.operationId = operationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes() {
|
||||||
|
byte[] feeBytes = fee.toBytes();
|
||||||
|
byte[] payerBytes = payer.toBytes();
|
||||||
|
List<Byte> requiredAuthsSerialized = new LinkedList<>();
|
||||||
|
if (this.requiredAuths != null) {
|
||||||
|
for (UserAccount userAccount : this.requiredAuths) {
|
||||||
|
requiredAuthsSerialized.addAll(Bytes.asList(userAccount.toBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] requiredAuthsBytes = Bytes.toArray(requiredAuthsSerialized);
|
||||||
|
byte[] requiredAuthsLength = {(byte)this.requiredAuths.size()};
|
||||||
|
byte[] operationIdBytes = Util.revertShort((short)operationId);
|
||||||
|
byte[] dataLength = Util.serializeLongToBytes(data.length());
|
||||||
|
byte[] dataBytes = Util.hexlify(data);
|
||||||
|
|
||||||
|
return Bytes.concat(feeBytes, payerBytes, requiredAuthsLength, requiredAuthsBytes, operationIdBytes, dataLength, dataBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJsonString() {
|
||||||
|
return toJsonObject().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJsonObject() {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
array.add(this.getId());
|
||||||
|
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
jsonObject.add(KEY_FEE, fee.toJsonObject());
|
||||||
|
jsonObject.addProperty(KEY_PAYER, payer.getObjectId());
|
||||||
|
JsonArray requiredAuthArray = new JsonArray();
|
||||||
|
if (requiredAuths != null) {
|
||||||
|
for (UserAccount userAccount : requiredAuths) {
|
||||||
|
requiredAuthArray.add(userAccount.getObjectId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsonObject.add(KEY_REQUIRED_AUTHS, requiredAuthArray);
|
||||||
|
jsonObject.addProperty(KEY_ID, operationId);
|
||||||
|
jsonObject.addProperty(KEY_DATA, Util.bytesToHex(Util.hexlify(data)));
|
||||||
|
|
||||||
|
array.add(jsonObject);
|
||||||
|
|
||||||
|
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<CustomOperation> {
|
||||||
|
|
||||||
|
@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<UserAccount> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -208,7 +208,7 @@ public class LimitOrderCreateOperation extends BaseOperation {
|
||||||
JsonObject jsonObject = json.getAsJsonObject();
|
JsonObject jsonObject = json.getAsJsonObject();
|
||||||
|
|
||||||
AssetAmount fee = context.deserialize(jsonObject.get(KEY_FEE), AssetAmount.class);
|
AssetAmount fee = context.deserialize(jsonObject.get(KEY_FEE), AssetAmount.class);
|
||||||
UserAccount seller = context.deserialize(jsonObject.get(KEY_SELLER), UserAccount.class);
|
UserAccount seller = new UserAccount(jsonObject.get(KEY_SELLER).getAsString());
|
||||||
AssetAmount amountToSell = context.deserialize(jsonObject.get(KEY_AMOUNT_TO_SELL), AssetAmount.class);
|
AssetAmount amountToSell = context.deserialize(jsonObject.get(KEY_AMOUNT_TO_SELL), AssetAmount.class);
|
||||||
AssetAmount minToReceive = context.deserialize(jsonObject.get(KEY_MIN_TO_RECEIVE), AssetAmount.class);
|
AssetAmount minToReceive = context.deserialize(jsonObject.get(KEY_MIN_TO_RECEIVE), AssetAmount.class);
|
||||||
String expiration = jsonObject.get(KEY_EXPIRATION).getAsString();
|
String expiration = jsonObject.get(KEY_EXPIRATION).getAsString();
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -24,6 +25,7 @@ import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||||
import cy.agorise.graphenej.models.BaseResponse;
|
import cy.agorise.graphenej.models.BaseResponse;
|
||||||
import cy.agorise.graphenej.models.WitnessResponse;
|
import cy.agorise.graphenej.models.WitnessResponse;
|
||||||
import cy.agorise.graphenej.objects.Memo;
|
import cy.agorise.graphenej.objects.Memo;
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCancelOperation;
|
import cy.agorise.graphenej.operations.LimitOrderCancelOperation;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
|
@ -53,6 +55,13 @@ public class TransactionTest {
|
||||||
private AssetAmount minToReceive = new AssetAmount(UnsignedLong.valueOf(520), BIT_USD);
|
private AssetAmount minToReceive = new AssetAmount(UnsignedLong.valueOf(520), BIT_USD);
|
||||||
private long expiration;
|
private long expiration;
|
||||||
|
|
||||||
|
// Custom operation transaction
|
||||||
|
private final AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(100000), CORE_ASSET);
|
||||||
|
private final UserAccount payer = bilthon_7;
|
||||||
|
private final Integer operationId = 61166;
|
||||||
|
private final List<UserAccount> requiredAuths = Collections.singletonList(payer);
|
||||||
|
private final String data = "some data";
|
||||||
|
|
||||||
private final long FEE_AMOUNT = 21851;
|
private final long FEE_AMOUNT = 21851;
|
||||||
|
|
||||||
// Lock object
|
// Lock object
|
||||||
|
@ -322,4 +331,19 @@ public class TransactionTest {
|
||||||
}
|
}
|
||||||
}, lockObject);
|
}, lockObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomOperationTransaction(){
|
||||||
|
ECKey sourcePrivateKey = new BrainKey(BILTHON_7_BRAIN_KEY, 0).getPrivateKey();
|
||||||
|
|
||||||
|
// Creating custom operation
|
||||||
|
CustomOperation customOperation = new CustomOperation(fee, payer, operationId, requiredAuths, data);
|
||||||
|
|
||||||
|
// Adding operation to the operation list
|
||||||
|
ArrayList<BaseOperation> operationList = new ArrayList<>();
|
||||||
|
operationList.add(customOperation);
|
||||||
|
|
||||||
|
// Broadcasting transaction
|
||||||
|
broadcastTransaction(sourcePrivateKey, operationList, listener, null);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||||
import cy.agorise.graphenej.models.BaseResponse;
|
import cy.agorise.graphenej.models.BaseResponse;
|
||||||
import cy.agorise.graphenej.models.Block;
|
import cy.agorise.graphenej.models.Block;
|
||||||
import cy.agorise.graphenej.models.WitnessResponse;
|
import cy.agorise.graphenej.models.WitnessResponse;
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -21,29 +22,42 @@ import java.util.concurrent.TimeoutException;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
|
|
||||||
public class GetBlockTest extends BaseApiTest {
|
public class GetBlockTest extends BaseApiTest {
|
||||||
private final static long BLOCK_NUMBER = 15776988L;
|
// data for the test with transfer operation
|
||||||
|
private final static long TRANSFER_BLOCK_NUMBER = 15776988L;
|
||||||
|
|
||||||
private final static String EXPECTED_PREVIOUS = "00f0bcdbe3d9dc66d8597e7b013a16d8dea5f778";
|
private final static String TRANSFER_EXPECTED_PREVIOUS = "00f0bcdbe3d9dc66d8597e7b013a16d8dea5f778";
|
||||||
private final static String EXPECTED_TIMESTAMP = "2017-04-18T04:14:51";
|
private final static String TRANSFER_EXPECTED_TIMESTAMP = "2017-04-18T04:14:51";
|
||||||
private final static String EXPECTED_WITNESS = "1.6.17";
|
private final static String TRANSFER_EXPECTED_WITNESS = "1.6.17";
|
||||||
private final static String EXPECTED_TRANSACTION_MERKLE_ROOT = "fb87a3f8af907a2450c327d181b2833b8270a504";
|
private final static String TRANSFER_EXPECTED_TRANSACTION_MERKLE_ROOT = "fb87a3f8af907a2450c327d181b2833b8270a504";
|
||||||
private final static int EXPECTED_EXTENSIONS_SIZE = 0;
|
private final static int TRANSFER_EXPECTED_EXTENSIONS_SIZE = 0;
|
||||||
private final static String EXPECTED_WITNESS_SIGNATURE = "20160921c8ba0d312dee14bdc780c3d05b27e406e2d014b8a2415e9843bf7075cb7abbc45f5173ffefac69cecf4dd2afa5dce6076bdf24cc577ff49427babe75e1";
|
private final static String TRANSFER_EXPECTED_WITNESS_SIGNATURE = "20160921c8ba0d312dee14bdc780c3d05b27e406e2d014b8a2415e9843bf7075cb7abbc45f5173ffefac69cecf4dd2afa5dce6076bdf24cc577ff49427babe75e1";
|
||||||
private final static int EXPECTED_TRANSACTIONS_SIZE = 1;
|
private final static int TRANSFER_EXPECTED_TRANSACTIONS_SIZE = 1;
|
||||||
private final static int EXPECTED_OPERATIONS_SIZE = 1;
|
private final static int TRANSFER_EXPECTED_OPERATIONS_SIZE = 1;
|
||||||
|
|
||||||
private final static UnsignedLong EXPECTED_FEE_AMOUNT = UnsignedLong.valueOf(2048);
|
private final static UnsignedLong TRANSFER_EXPECTED_FEE_AMOUNT = UnsignedLong.valueOf(2048);
|
||||||
private final static String EXPECTED_FEE_ASSET_ID = "1.3.113";
|
private final static String TRANSFER_EXPECTED_FEE_ASSET_ID = "1.3.113";
|
||||||
private final static String EXPECTED_FROM = "1.2.151069";
|
private final static String TRANSFER_EXPECTED_FROM = "1.2.151069";
|
||||||
private final static String EXPECTED_TO = "1.2.116354";
|
private final static String TRANSFER_EXPECTED_TO = "1.2.116354";
|
||||||
private final static UnsignedLong EXPECTED_AMOUNT = UnsignedLong.valueOf(13700);
|
private final static UnsignedLong TRANSFER_EXPECTED_AMOUNT = UnsignedLong.valueOf(13700);
|
||||||
private final static String EXPECTED_ASSET_ID = "1.3.113";
|
private final static String TRANSFER_EXPECTED_ASSET_ID = "1.3.113";
|
||||||
|
|
||||||
|
// data for the test with custom operation
|
||||||
|
private final static long CUSTOM_BLOCK_NUMBER = 22754473L;
|
||||||
|
private final static int CUSTOM_TRANSACTION_INDEX = 8;
|
||||||
|
|
||||||
|
private final static UnsignedLong CUSTOM_EXPECTED_FEE_AMOUNT = UnsignedLong.valueOf(13798);
|
||||||
|
private final static String CUSTOM_EXPECTED_FEE_ASSET_ID = "1.3.0";
|
||||||
|
private final static String CUSTOM_EXPECTED_PAYER = "1.2.140994";
|
||||||
|
private final static int CUSTOM_EXPECTED_REQUIRED_AUTHS_SIZE = 1;
|
||||||
|
private final static String CUSTOM_EXPECTED_REQUIRED_AUTH = "1.2.140994";
|
||||||
|
private final static int CUSTOM_EXPECTED_ID = 61166;
|
||||||
|
private final static String CUSTOM_EXPECTED_DATA = "some data";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetBlock() {
|
public void testGetBlockWithTransferOperation() {
|
||||||
try {
|
try {
|
||||||
final Exchanger<Object> responseExchanger = new Exchanger<>();
|
final Exchanger<Object> responseExchanger = new Exchanger<>();
|
||||||
mWebSocket.addListener(new GetBlock(BLOCK_NUMBER, new WitnessResponseListener() {
|
mWebSocket.addListener(new GetBlock(TRANSFER_BLOCK_NUMBER, new WitnessResponseListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(WitnessResponse response) {
|
public void onSuccess(WitnessResponse response) {
|
||||||
System.out.println("onSuccess");
|
System.out.println("onSuccess");
|
||||||
|
@ -71,31 +85,88 @@ public class GetBlockTest extends BaseApiTest {
|
||||||
|
|
||||||
Block block = (Block) responseResult;
|
Block block = (Block) responseResult;
|
||||||
Assert.assertNotNull(block);
|
Assert.assertNotNull(block);
|
||||||
Assert.assertEquals(EXPECTED_PREVIOUS, block.getPrevious());
|
Assert.assertEquals(TRANSFER_EXPECTED_PREVIOUS, block.getPrevious());
|
||||||
Assert.assertEquals(EXPECTED_TIMESTAMP, block.getTimestamp());
|
Assert.assertEquals(TRANSFER_EXPECTED_TIMESTAMP, block.getTimestamp());
|
||||||
Assert.assertEquals(EXPECTED_WITNESS, block.getWitness());
|
Assert.assertEquals(TRANSFER_EXPECTED_WITNESS, block.getWitness());
|
||||||
Assert.assertEquals(EXPECTED_TRANSACTION_MERKLE_ROOT, block.getTransaction_merkle_root());
|
Assert.assertEquals(TRANSFER_EXPECTED_TRANSACTION_MERKLE_ROOT, block.getTransaction_merkle_root());
|
||||||
Assert.assertEquals(EXPECTED_EXTENSIONS_SIZE, block.getExtensions().length);
|
Assert.assertEquals(TRANSFER_EXPECTED_EXTENSIONS_SIZE, block.getExtensions().length);
|
||||||
Assert.assertEquals(EXPECTED_WITNESS_SIGNATURE, block.getWitness_signature());
|
Assert.assertEquals(TRANSFER_EXPECTED_WITNESS_SIGNATURE, block.getWitness_signature());
|
||||||
|
|
||||||
List<Transaction> transactions = block.getTransactions();
|
List<Transaction> transactions = block.getTransactions();
|
||||||
Assert.assertEquals(EXPECTED_TRANSACTIONS_SIZE, transactions.size());
|
Assert.assertEquals(TRANSFER_EXPECTED_TRANSACTIONS_SIZE, transactions.size());
|
||||||
|
|
||||||
List<BaseOperation> operations = transactions.get(0).getOperations();
|
List<BaseOperation> operations = transactions.get(0).getOperations();
|
||||||
Assert.assertEquals(EXPECTED_OPERATIONS_SIZE, operations.size());
|
Assert.assertEquals(TRANSFER_EXPECTED_OPERATIONS_SIZE, operations.size());
|
||||||
|
|
||||||
BaseOperation operation = operations.get(0);
|
BaseOperation operation = operations.get(0);
|
||||||
Assert.assertThat(operation, instanceOf(TransferOperation.class));
|
Assert.assertThat(operation, instanceOf(TransferOperation.class));
|
||||||
|
|
||||||
TransferOperation transferOperation = (TransferOperation) operation;
|
TransferOperation transferOperation = (TransferOperation) operation;
|
||||||
AssetAmount fee = transferOperation.getFee();
|
AssetAmount fee = transferOperation.getFee();
|
||||||
Assert.assertEquals(EXPECTED_FEE_AMOUNT, fee.getAmount());
|
Assert.assertEquals(TRANSFER_EXPECTED_FEE_AMOUNT, fee.getAmount());
|
||||||
Assert.assertEquals(EXPECTED_FEE_ASSET_ID, fee.getAsset().getObjectId());
|
Assert.assertEquals(TRANSFER_EXPECTED_FEE_ASSET_ID, fee.getAsset().getObjectId());
|
||||||
Assert.assertEquals(EXPECTED_FROM, transferOperation.getFrom().getObjectId());
|
Assert.assertEquals(TRANSFER_EXPECTED_FROM, transferOperation.getFrom().getObjectId());
|
||||||
Assert.assertEquals(EXPECTED_TO, transferOperation.getTo().getObjectId());
|
Assert.assertEquals(TRANSFER_EXPECTED_TO, transferOperation.getTo().getObjectId());
|
||||||
AssetAmount assetAmount = transferOperation.getAssetAmount();
|
AssetAmount assetAmount = transferOperation.getAssetAmount();
|
||||||
Assert.assertEquals(EXPECTED_AMOUNT, assetAmount.getAmount());
|
Assert.assertEquals(TRANSFER_EXPECTED_AMOUNT, assetAmount.getAmount());
|
||||||
Assert.assertEquals(EXPECTED_ASSET_ID, assetAmount.getAsset().getObjectId());
|
Assert.assertEquals(TRANSFER_EXPECTED_ASSET_ID, assetAmount.getAsset().getObjectId());
|
||||||
|
} catch (WebSocketException e) {
|
||||||
|
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
||||||
|
Assert.fail("Fail because of WebSocketException");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("InterruptedException. Msg: " + e.getMessage());
|
||||||
|
Assert.fail("Fail because of InterruptedException");
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
System.out.println("TimeoutException. Msg: " + e.getMessage());
|
||||||
|
Assert.fail("Fail because of TimeoutException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBlockWithCustomOperation() {
|
||||||
|
try {
|
||||||
|
final Exchanger<Object> responseExchanger = new Exchanger<>();
|
||||||
|
mWebSocket.addListener(new GetBlock(CUSTOM_BLOCK_NUMBER, new WitnessResponseListener() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(WitnessResponse response) {
|
||||||
|
System.out.println("onSuccess");
|
||||||
|
try {
|
||||||
|
responseExchanger.exchange(response.result);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("InterruptedException in success handler. Msg: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(BaseResponse.Error error) {
|
||||||
|
System.out.println("onError");
|
||||||
|
try {
|
||||||
|
responseExchanger.exchange(null);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("InterruptedException in error handler. Msg: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
mWebSocket.connect();
|
||||||
|
|
||||||
|
Object responseResult = responseExchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
Block block = (Block) responseResult;
|
||||||
|
List<Transaction> transactions = block.getTransactions();
|
||||||
|
List<BaseOperation> operations = transactions.get(CUSTOM_TRANSACTION_INDEX).getOperations();
|
||||||
|
BaseOperation operation = operations.get(0);
|
||||||
|
Assert.assertThat(operation, instanceOf(CustomOperation.class));
|
||||||
|
|
||||||
|
CustomOperation customOperation = (CustomOperation) operation;
|
||||||
|
AssetAmount fee = customOperation.getFee();
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_FEE_AMOUNT, fee.getAmount());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_FEE_ASSET_ID, fee.getAsset().getObjectId());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_PAYER, customOperation.getPayer().getObjectId());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_REQUIRED_AUTHS_SIZE, customOperation.getRequiredAuths().size());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_REQUIRED_AUTH, customOperation.getRequiredAuths().get(0).getObjectId());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_ID, customOperation.getOperationId());
|
||||||
|
Assert.assertEquals(CUSTOM_EXPECTED_DATA, customOperation.getData());
|
||||||
} catch (WebSocketException e) {
|
} catch (WebSocketException e) {
|
||||||
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
||||||
Assert.fail("Fail because of WebSocketException");
|
Assert.fail("Fail because of WebSocketException");
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package cy.agorise.graphenej.operations;
|
||||||
|
|
||||||
|
import com.google.common.primitives.UnsignedLong;
|
||||||
|
import cy.agorise.graphenej.Asset;
|
||||||
|
import cy.agorise.graphenej.AssetAmount;
|
||||||
|
import cy.agorise.graphenej.UserAccount;
|
||||||
|
import cy.agorise.graphenej.Util;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
public class CustomOperationTest {
|
||||||
|
private final Asset CORE_ASSET = new Asset("1.3.0");
|
||||||
|
private final AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(100000L), CORE_ASSET);
|
||||||
|
private final UserAccount payer = new UserAccount("1.2.20");
|
||||||
|
private final Integer operationId = 61166;
|
||||||
|
private final List<UserAccount> requiredAuths = Collections.singletonList(payer);
|
||||||
|
private final String shortData = "some data";
|
||||||
|
private final String longData = "very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data, very long data...";
|
||||||
|
|
||||||
|
private static final byte[] EXPECTED_SERIALIZED_BYTES_1 = Util.hexToBytes("a08601000000000000140114eeee09736f6d652064617461");
|
||||||
|
private static final byte[] EXPECTED_SERIALIZED_BYTES_2 = Util.hexToBytes("a08601000000000000140114eeeee50176657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c202076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c202076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c202076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612c202076657279206c6f6e6720646174612c2076657279206c6f6e6720646174612e2e2e");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToBytes() throws Exception {
|
||||||
|
CustomOperation customOperation1 = new CustomOperation(fee, payer, operationId, requiredAuths, shortData);
|
||||||
|
byte[] serialized1 = customOperation1.toBytes();
|
||||||
|
assertArrayEquals(EXPECTED_SERIALIZED_BYTES_1, serialized1);
|
||||||
|
|
||||||
|
// test with some long data string to check if data length is serialized correctly
|
||||||
|
CustomOperation customOperation2 = new CustomOperation(fee, payer, operationId, requiredAuths, longData);
|
||||||
|
byte[] serialized2 = customOperation2.toBytes();
|
||||||
|
assertArrayEquals(EXPECTED_SERIALIZED_BYTES_2, serialized2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue