8 | * Generic class used to represent a graphene object as defined in
9 | *
10 | *
11 | * Created by nelson on 11/8/16.
12 | */
13 | public class GrapheneObject {
14 | public static final String KEY_ID = "id";
15 |
16 | public static final int PROTOCOL_SPACE = 1;
17 | public static final int IMPLEMENTATION_SPACE = 2;
18 |
19 | @Expose
20 | protected String id;
21 |
22 | protected int space;
23 | protected int type;
24 | protected long instance;
25 |
26 | public GrapheneObject(String id){
27 | this.id = id;
28 | String[] parts = id.split("\\.");
29 | if(parts.length == 3){
30 | this.space = Integer.parseInt(parts[0]);
31 | this.type = Integer.parseInt(parts[1]);
32 | this.instance = Long.parseLong(parts[2]);
33 | }
34 | }
35 |
36 | /**
37 | *
38 | * @return: A String containing the full object apiId in the form {space}.{type}.{instance}
39 | */
40 | public String getObjectId(){
41 | return String.format("%d.%d.%d", space, type, instance);
42 | }
43 |
44 | /**
45 | * Returns the type of this object.
46 | * @return: Instance of the ObjectType enum.
47 | */
48 | public ObjectType getObjectType(){
49 | switch(space){
50 | case PROTOCOL_SPACE:
51 | switch(type){
52 | case 1:
53 | return ObjectType.BASE_OBJECT;
54 | case 2:
55 | return ObjectType.ACCOUNT_OBJECT;
56 | case 3:
57 | return ObjectType.ASSET_OBJECT;
58 | case 4:
59 | return ObjectType.FORCE_SETTLEMENT_OBJECT;
60 | case 5:
61 | return ObjectType.COMMITTEE_MEMBER_OBJECT;
62 | case 6:
63 | return ObjectType.WITNESS_OBJECT;
64 | case 7:
65 | return ObjectType.LIMIT_ORDER_OBJECT;
66 | case 8:
67 | return ObjectType.CALL_ORDER_OBJECT;
68 | case 9:
69 | return ObjectType.CUSTOM_OBJECT;
70 | case 10:
71 | return ObjectType.PROPOSAL_OBJECT;
72 | case 11:
73 | return ObjectType.OPERATION_HISTORY_OBJECT;
74 | case 12:
75 | return ObjectType.WITHDRAW_PERMISSION_OBJECT;
76 | case 13:
77 | return ObjectType.VESTING_BALANCE_OBJECT;
78 | case 14:
79 | return ObjectType.WORKER_OBJECT;
80 | case 15:
81 | return ObjectType.BALANCE_OBJECT;
82 | }
83 | case IMPLEMENTATION_SPACE:
84 | switch(type){
85 | case 0:
86 | return ObjectType.GLOBAL_PROPERTY_OBJECT;
87 | case 1:
88 | return ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT;
89 | case 3:
90 | return ObjectType.ASSET_DYNAMIC_DATA;
91 | case 4:
92 | return ObjectType.ASSET_BITASSET_DATA;
93 | case 5:
94 | return ObjectType.ACCOUNT_BALANCE_OBJECT;
95 | case 6:
96 | return ObjectType.ACCOUNT_STATISTICS_OBJECT;
97 | case 7:
98 | return ObjectType.TRANSACTION_OBJECT;
99 | case 8:
100 | return ObjectType.BLOCK_SUMMARY_OBJECT;
101 | case 9:
102 | return ObjectType.ACCOUNT_TRANSACTION_HISTORY_OBJECT;
103 | case 10:
104 | return ObjectType.BLINDED_BALANCE_OBJECT;
105 | case 11:
106 | return ObjectType.CHAIN_PROPERTY_OBJECT;
107 | case 12:
108 | return ObjectType.WITNESS_SCHEDULE_OBJECT;
109 | case 13:
110 | return ObjectType.BUDGET_RECORD_OBJECT;
111 | case 14:
112 | return ObjectType.SPECIAL_AUTHORITY_OBJECT;
113 | }
114 | }
115 | return null;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/objects/LimitOrder.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.objects;
2 |
3 | import com.google.gson.*;
4 | import com.gxchain.client.graphenej.Price;
5 | import com.gxchain.client.graphenej.Varint;
6 | import com.gxchain.client.graphenej.interfaces.ByteSerializable;
7 |
8 | import java.io.ByteArrayOutputStream;
9 | import java.io.DataOutput;
10 | import java.io.DataOutputStream;
11 | import java.io.IOException;
12 | import java.lang.reflect.Type;
13 |
14 | /**
15 | *
16 | * @author henry
17 | */
18 | public class LimitOrder extends GrapheneObject implements ByteSerializable {
19 |
20 | public static final String KEY_EXPIRATION = "expiration";
21 | public static final String KEY_SELLER = "seller";
22 | public static final String KEY_FOR_SALE = "for_sale";
23 | public static final String KEY_DEFERRED_FEE = "deferred_fee";
24 | public static final String KEY_PRICE = "sell_price";
25 |
26 | private String expiration;
27 | private UserAccount seller;
28 | private long forSale;
29 | private long deferredFee;
30 | private Price sellPrice;
31 |
32 | public LimitOrder(String id) {
33 | super(id);
34 | }
35 |
36 | public String getExpiration() {
37 | return expiration;
38 | }
39 |
40 | public void setExpiration(String expiration) {
41 | this.expiration = expiration;
42 | }
43 |
44 | public UserAccount getSeller() {
45 | return seller;
46 | }
47 |
48 | public void setSeller(UserAccount seller) {
49 | this.seller = seller;
50 | }
51 |
52 | public long getForSale() {
53 | return forSale;
54 | }
55 |
56 | public void setForSale(long forSale) {
57 | this.forSale = forSale;
58 | }
59 |
60 | public long getDeferredFee() {
61 | return deferredFee;
62 | }
63 |
64 | public void setDeferredFee(long deferredFee) {
65 | this.deferredFee = deferredFee;
66 | }
67 |
68 | public Price getSellPrice() {
69 | return sellPrice;
70 | }
71 |
72 | public void setSellPrice(Price sellPrice) {
73 | this.sellPrice = sellPrice;
74 | }
75 |
76 | @Override
77 | public byte[] toBytes() {
78 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
79 | DataOutput out = new DataOutputStream(byteArrayOutputStream);
80 | byte[] serialized = null;
81 | try {
82 | Varint.writeUnsignedVarLong(this.instance, out);
83 | serialized = byteArrayOutputStream.toByteArray();
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | }
87 | return serialized;
88 | }
89 |
90 | /**
91 | * Custom deserializer for the LimitOrder class, used to deserialize a json-formatted string in
92 | * the following format:
93 | *
94 | * {
95 | * "id": "1.7.2389233",
96 | * "expiration": "2017-04-21T15:40:04",
97 | * "seller": "1.2.114363",
98 | * "forSale": "10564959415",
99 | * "sell_price": {
100 | * "base": {
101 | * "amount": "10565237932",
102 | * "asset_id": "1.3.0"
103 | * },
104 | * "quote": {
105 | * "amount": 5803878,
106 | * "asset_id": "1.3.121"
107 | * }
108 | * },
109 | * "deferredFee": 0
110 | * }
111 | */
112 | public static class LimitOrderDeserializer implements JsonDeserializer {
113 |
114 | @Override
115 | public LimitOrder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
116 | JsonObject object = json.getAsJsonObject();
117 | String id = object.get(KEY_ID).getAsString();
118 | String expiration = object.get(KEY_EXPIRATION).getAsString();
119 | UserAccount seller = context.deserialize(object.get(KEY_SELLER), UserAccount.class);
120 | String forSale = object.get(KEY_FOR_SALE).getAsString();
121 | Price price = context.deserialize(object.get(KEY_PRICE), Price.class);
122 | long deferredFee = object.get(KEY_DEFERRED_FEE).getAsLong();
123 |
124 | LimitOrder limitOrder = new LimitOrder(id);
125 | limitOrder.setExpiration(expiration);
126 | limitOrder.setSeller(seller);
127 | limitOrder.setForSale(Long.parseLong(forSale));
128 | limitOrder.setSellPrice(price);
129 | limitOrder.setDeferredFee(deferredFee);
130 | return limitOrder;
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/objects/Optional.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.objects;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.gson.JsonElement;
5 | import com.gxchain.client.graphenej.interfaces.ByteSerializable;
6 | import com.gxchain.client.graphenej.interfaces.GrapheneSerializable;
7 |
8 | /**
9 | * Container template class used whenever we have an optional field.
10 | *
11 | * The idea here is that the binary serialization of this field should be performed
12 | * in a specific way determined by the field implementing the {@link ByteSerializable}
13 | * interface, more specifically using the {@link ByteSerializable#toBytes()} method.
14 | *
15 | * However, if the field is missing, the Optional class should be able to know how
16 | * to serialize it, as this is always done by placing an zero byte.
17 | */
18 | public class Optional implements GrapheneSerializable {
19 | private T optionalField;
20 |
21 | public Optional(T field) {
22 | optionalField = field;
23 | }
24 |
25 | @Override
26 | public byte[] toBytes() {
27 | if (optionalField == null)
28 | return new byte[]{(byte) 0};
29 | else
30 | return Bytes.concat(new byte[]{(byte) 1}, optionalField.toBytes());
31 | }
32 |
33 | public boolean isSet() {
34 | return this.optionalField != null;
35 | }
36 |
37 | @Override
38 | public String toJsonString() {
39 | return optionalField.toJsonString();
40 | }
41 |
42 | @Override
43 | public JsonElement toJsonObject() {
44 | return optionalField.toJsonObject();
45 | }
46 |
47 | public T value(){
48 | return optionalField;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/objects/Vote.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.objects;
2 |
3 | import com.gxchain.client.graphenej.interfaces.ByteSerializable;
4 | import lombok.Getter;
5 |
6 | /**
7 | * Created by nelson on 12/5/16.
8 | */
9 | public class Vote implements ByteSerializable {
10 | @Getter
11 | private int type;
12 | @Getter
13 | private int instance;
14 |
15 | public Vote(String vote) {
16 | String[] parts = vote.split(":");
17 | assert (parts.length == 2);
18 | this.type = Integer.valueOf(parts[0]);
19 | this.instance = Integer.valueOf(parts[1]);
20 | }
21 |
22 | public Vote(int type, int instance) {
23 | this.type = type;
24 | this.instance = instance;
25 | }
26 |
27 | @Override
28 | public String toString() {
29 | return String.format("%d:%d", this.type, this.instance);
30 | }
31 |
32 | @Override
33 | public byte[] toBytes() {
34 | return new byte[]{(byte) this.instance, (byte) this.type};
35 | }
36 |
37 | @Override
38 | public boolean equals(final Object obj) {
39 | if (obj == null) {
40 | return false;
41 | }
42 | final Vote vote = (Vote) obj;
43 | if (this == vote) {
44 | return true;
45 | } else {
46 | return (this.type == vote.type && this.instance == vote.instance);
47 | }
48 | }
49 |
50 | public int hashCode() {
51 | return type * 10000000 + instance;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/AccountUpdateOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.common.primitives.UnsignedLong;
5 | import com.google.gson.Gson;
6 | import com.google.gson.JsonArray;
7 | import com.google.gson.JsonElement;
8 | import com.google.gson.JsonObject;
9 | import com.gxchain.client.graphenej.enums.OperationType;
10 | import com.gxchain.client.graphenej.objects.*;
11 |
12 | /**
13 | * Class used to encapsulate operations related to the ACCOUNT_UPDATE_OPERATION.
14 | */
15 | public class AccountUpdateOperation extends BaseOperation {
16 | public static final String KEY_ACCOUNT = "account";
17 | public static final String KEY_OWNER = "owner";
18 | public static final String KEY_ACTIVE = "active";
19 | public static final String KEY_FEE = "fee";
20 | public static final String KEY_NEW_OPTIONS = "new_options";
21 | public static final String KEY_EXTENSIONS = "extensions";
22 |
23 | private AssetAmount fee;
24 | private UserAccount account;
25 | private Optional owner;
26 | private Optional active;
27 | private Optional new_options;
28 |
29 | /**
30 | * Account update operation constructor.
31 | * @param account User account to update. Can't be null.
32 | * @param owner Owner authority to set. Can be null.
33 | * @param active Active authority to set. Can be null.
34 | * @param options Active authority to set. Can be null.
35 | * @param fee The fee to pay. Can be null.
36 | */
37 | public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options, AssetAmount fee){
38 | super(OperationType.ACCOUNT_UPDATE_OPERATION);
39 | this.fee = fee;
40 | this.account = account;
41 | this.owner = new Optional<>(owner);
42 | this.active = new Optional<>(active);
43 | this.new_options = new Optional<>(options);
44 | extensions = new Extensions();
45 | }
46 |
47 | public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options){
48 | this(account, owner, active, options, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0")));
49 | }
50 |
51 | @Override
52 | public void setFee(AssetAmount fee){
53 | this.fee = fee;
54 | }
55 |
56 | public void setOwner(Authority owner){
57 | this.owner = new Optional<>(owner);
58 | }
59 |
60 | public void setActive(Authority active){
61 | this.active = new Optional<>(active);
62 | }
63 |
64 | public void setAccountOptions(AccountOptions options){
65 | this.new_options = new Optional<>(options);
66 | }
67 |
68 | @Override
69 | public String toJsonString() {
70 | Gson gson = new Gson();
71 | return gson.toJson(this);
72 | }
73 |
74 | @Override
75 | public JsonElement toJsonObject() {
76 | JsonArray array = new JsonArray();
77 | array.add(this.getId());
78 |
79 | JsonObject accountUpdate = new JsonObject();
80 | accountUpdate.add(KEY_FEE, fee.toJsonObject());
81 | accountUpdate.addProperty(KEY_ACCOUNT, account.getObjectId());
82 | if(owner.isSet())
83 | accountUpdate.add(KEY_OWNER, owner.toJsonObject());
84 | if(active.isSet())
85 | accountUpdate.add(KEY_ACTIVE, active.toJsonObject());
86 | if(new_options.isSet())
87 | accountUpdate.add(KEY_NEW_OPTIONS, new_options.toJsonObject());
88 | accountUpdate.add(KEY_EXTENSIONS, extensions.toJsonObject());
89 | array.add(accountUpdate);
90 | return array;
91 | }
92 |
93 | @Override
94 | public byte[] toBytes() {
95 | byte[] feeBytes = fee.toBytes();
96 | byte[] accountBytes = account.toBytes();
97 | byte[] ownerBytes = owner.toBytes();
98 | byte[] activeBytes = active.toBytes();
99 | byte[] newOptionsBytes = new_options.toBytes();
100 | byte[] extensionBytes = extensions.toBytes();
101 | return Bytes.concat(feeBytes, accountBytes, ownerBytes, activeBytes, newOptionsBytes, extensionBytes);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/AccountUpdateOperationBuilder.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.gxchain.client.graphenej.errors.MalformedOperationException;
4 | import com.gxchain.client.graphenej.objects.AccountOptions;
5 | import com.gxchain.client.graphenej.objects.AssetAmount;
6 | import com.gxchain.client.graphenej.objects.Authority;
7 | import com.gxchain.client.graphenej.objects.UserAccount;
8 |
9 | /**
10 | * Created by nelson on 3/1/17.
11 | */
12 | public class AccountUpdateOperationBuilder extends BaseOperationBuilder {
13 | private AssetAmount fee;
14 | private UserAccount account;
15 | private Authority owner;
16 | private Authority active;
17 | private AccountOptions new_options;
18 |
19 | public AccountUpdateOperationBuilder setFee(AssetAmount fee) {
20 | this.fee = fee;
21 | return this;
22 | }
23 |
24 | public AccountUpdateOperationBuilder setAccount(UserAccount account) {
25 | this.account = account;
26 | return this;
27 | }
28 |
29 | public AccountUpdateOperationBuilder setOwner(Authority owner) {
30 | this.owner = owner;
31 | return this;
32 | }
33 |
34 | public AccountUpdateOperationBuilder setActive(Authority active) {
35 | this.active = active;
36 | return this;
37 | }
38 |
39 | public AccountUpdateOperationBuilder setOptions(AccountOptions newOptions) {
40 | this.new_options = newOptions;
41 | return this;
42 | }
43 |
44 | @Override
45 | public AccountUpdateOperation build() {
46 | AccountUpdateOperation operation;
47 | if(this.account == null){
48 | throw new MalformedOperationException("This operation requires an account to be set");
49 | }else{
50 | if(owner != null || active != null || new_options != null){
51 | if(fee == null){
52 | operation = new AccountUpdateOperation(account, owner, active, new_options);
53 | }else{
54 | operation = new AccountUpdateOperation(account, owner, active, new_options, fee);
55 | }
56 | }else{
57 | throw new MalformedOperationException("This operation requires at least either an authority or account options change");
58 | }
59 | }
60 | return operation;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/BaseOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.gson.JsonArray;
4 | import com.google.gson.JsonElement;
5 | import com.gxchain.client.graphenej.enums.OperationType;
6 | import com.gxchain.client.graphenej.interfaces.ByteSerializable;
7 | import com.gxchain.client.graphenej.interfaces.JsonSerializable;
8 | import com.gxchain.client.graphenej.objects.AssetAmount;
9 | import com.gxchain.client.graphenej.objects.Extensions;
10 |
11 | /**
12 | * Created by nelson on 11/5/16.
13 | */
14 | public abstract class BaseOperation implements ByteSerializable, JsonSerializable {
15 |
16 | public static final String KEY_FEE = "fee";
17 | public static final String KEY_EXTENSIONS = "extensions";
18 |
19 | protected OperationType type;
20 | protected Extensions extensions;
21 |
22 | public BaseOperation(OperationType type){
23 | this.type = type;
24 | this.extensions = new Extensions();
25 | }
26 |
27 | public byte getId() {
28 | return (byte) this.type.getCode();
29 | }
30 |
31 | public abstract void setFee(AssetAmount assetAmount);
32 |
33 | public JsonElement toJsonObject(){
34 | JsonArray array = new JsonArray();
35 | array.add(this.getId());
36 | return array;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/BaseOperationBuilder.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | /**
4 | * Base template for all operation-specific factory classes.
5 | */
6 | public abstract class BaseOperationBuilder {
7 |
8 | /**
9 | * Must be implemented and return the specific operation the
10 | * factory is supposed to build.
11 | *
12 | * @return: A usable instance of a given operation.
13 | */
14 | public abstract BaseOperation build();
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/BroadcastOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.gson.JsonArray;
5 | import com.google.gson.JsonElement;
6 | import com.google.gson.JsonObject;
7 | import com.gxchain.client.graphenej.enums.OperationType;
8 | import com.gxchain.client.graphenej.objects.AssetAmount;
9 | import com.gxchain.client.graphenej.objects.BroadcastRequestParams;
10 | import com.gxchain.client.graphenej.objects.Extensions;
11 | import lombok.Data;
12 |
13 | /**
14 | * @Description
15 | * @Author Hanawa
16 | * @Date 2018/3/6
17 | * @Version 1.0
18 | */
19 | @Data public class BroadcastOperation extends BaseOperation {
20 | private static final long serialVersionUID = 4544825479406548507L;
21 | private String proxyMemo;
22 | private AssetAmount fee;
23 | private BroadcastRequestParams requestParams;
24 | private Extensions extensions;
25 |
26 | public BroadcastOperation() {
27 | super(OperationType.BROADCAST_STORE_DATA);
28 | }
29 |
30 | @Override public void setFee(AssetAmount assetAmount) {
31 | this.fee = assetAmount;
32 | }
33 |
34 | @Override public byte[] toBytes() {
35 | byte[] proxyMemoPrefix = new byte[] {(byte) proxyMemo.length()};
36 | byte[] proxyMemoBytes = proxyMemo.getBytes();
37 | byte[] feeBytes = fee.toBytes();
38 | byte[] requestParamsBytes = requestParams.toBytes();
39 | byte[] extensionsBytes = extensions.toBytes();
40 |
41 | return Bytes.concat(proxyMemoPrefix, proxyMemoBytes, feeBytes, requestParamsBytes, extensionsBytes);
42 | }
43 |
44 | @Override public String toJsonString() {
45 | return toJsonObject().toString();
46 | }
47 |
48 | @Override public JsonElement toJsonObject() {
49 | JsonArray array = new JsonArray();
50 | array.add(this.getId());
51 | JsonObject jsonObject = new JsonObject();
52 | jsonObject.addProperty("proxy_memo", proxyMemo);
53 | jsonObject.add("fee", fee.toJsonObject());
54 | jsonObject.add("request_params",requestParams.toJsonObject());
55 | jsonObject.add("extensions", new JsonArray());
56 | array.add(jsonObject);
57 | return array;
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/CallContractOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.gson.JsonArray;
4 | import com.google.gson.JsonElement;
5 | import com.google.gson.JsonObject;
6 | import com.gxchain.client.graphenej.enums.OperationType;
7 | import com.gxchain.client.graphenej.objects.AssetAmount;
8 | import com.gxchain.client.graphenej.objects.Optional;
9 | import com.gxchain.client.graphenej.objects.UserAccount;
10 | import lombok.Data;
11 |
12 | /**
13 | * @author liruobin
14 | * @since 2019/2/15 8:59 PM
15 | */
16 | @Data
17 | public class CallContractOperation extends BaseOperation {
18 |
19 | private AssetAmount fee;
20 |
21 | private UserAccount account;
22 |
23 | private UserAccount contractId;
24 |
25 | private String methodName;
26 |
27 | private Optional amount;
28 |
29 | private String data;
30 |
31 | public CallContractOperation(AssetAmount fee, UserAccount account, UserAccount contractId, String methodName, AssetAmount amount, String data) {
32 | super(OperationType.CALL_CONTRACT);
33 | this.fee = fee;
34 | this.account = account;
35 | this.contractId = contractId;
36 | this.methodName = methodName;
37 | this.amount = new Optional<>(amount);
38 | this.data = data;
39 | }
40 |
41 | @Override
42 | public byte[] toBytes() {
43 | return new byte[0];
44 | }
45 |
46 | @Override
47 | public String toJsonString() {
48 | return toJsonObject().toString();
49 | }
50 |
51 | public JsonElement toJsonObject() {
52 | JsonArray array = new JsonArray();
53 | array.add(this.getId());
54 | JsonObject jsonObject = new JsonObject();
55 | if (fee != null)
56 | jsonObject.add("fee", fee.toJsonObject());
57 | jsonObject.addProperty("account", account.getObjectId());
58 | jsonObject.addProperty("method_name", methodName);
59 | jsonObject.addProperty("contract_id", contractId.getObjectId());
60 | jsonObject.addProperty("data", data);
61 | if (amount.isSet() && amount.value().getAmount().longValue() != 0) {
62 | jsonObject.add("amount", amount.toJsonObject());
63 | }
64 | jsonObject.add(KEY_EXTENSIONS, new JsonArray());
65 | array.add(jsonObject);
66 | return array;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/DiyOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.gson.JsonArray;
5 | import com.google.gson.JsonElement;
6 | import com.google.gson.JsonObject;
7 | import com.gxchain.client.graphenej.Util;
8 | import com.gxchain.client.graphenej.Varint;
9 | import com.gxchain.client.graphenej.enums.OperationType;
10 | import com.gxchain.client.graphenej.objects.AssetAmount;
11 | import com.gxchain.client.graphenej.objects.Extensions;
12 | import com.gxchain.client.graphenej.objects.UserAccount;
13 | import lombok.Data;
14 |
15 | /**
16 | * @Description
17 | * @Author Hanawa
18 | * @Date 2018/8/7
19 | * @Version 1.0
20 | */
21 | @Data
22 | public class DiyOperation extends BaseOperation {
23 |
24 | private AssetAmount fee;
25 | private UserAccount payer;
26 | private Extensions requiredAuths;
27 | private int d;
28 | private String data;
29 |
30 | public DiyOperation() {
31 | super(OperationType.DIY_OPERATION);
32 | }
33 |
34 | @Override
35 | public byte[] toBytes() {
36 | byte[] feeBytes = fee.toBytes();
37 | byte[] payerBytes = payer.toBytes();
38 | byte[] requireAuthsBytes = requiredAuths.toBytes();
39 | byte[] idBytes = Varint.writeUnsignedSize(d);
40 | byte[] dataPrefix = Varint.writeUnsignedVarInt(data.getBytes().length);
41 | byte[] dataBytes = data.getBytes();
42 | return Bytes.concat(feeBytes, payerBytes, requireAuthsBytes, idBytes, dataPrefix, dataBytes);
43 | }
44 |
45 | @Override
46 | public String toJsonString() {
47 | return toJsonObject().toString();
48 | }
49 |
50 | @Override
51 | public JsonElement toJsonObject() {
52 | JsonArray array = new JsonArray();
53 | array.add(this.getId());
54 | JsonObject jsonObject = new JsonObject();
55 | if (fee != null)
56 | jsonObject.add("fee", fee.toJsonObject());
57 | jsonObject.addProperty("payer", payer.getObjectId());
58 | jsonObject.add("required_auths", new JsonArray());
59 | jsonObject.addProperty("id", d);
60 | jsonObject.addProperty("data", Util.bytesToHex(data.getBytes()));
61 | array.add(jsonObject);
62 | return array;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/LimitOrderCancelOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.gson.JsonArray;
5 | import com.google.gson.JsonElement;
6 | import com.google.gson.JsonObject;
7 | import com.gxchain.client.graphenej.enums.OperationType;
8 | import com.gxchain.client.graphenej.objects.AssetAmount;
9 | import com.gxchain.client.graphenej.objects.LimitOrder;
10 | import com.gxchain.client.graphenej.objects.UserAccount;
11 |
12 | /**
13 | * Created by nelson on 3/21/17.
14 | */
15 | public class LimitOrderCancelOperation extends BaseOperation {
16 |
17 | // Constants used in the JSON representation
18 | public static final String KEY_FEE_PAYING_ACCOUNT = "fee_paying_account";
19 | public static final String KEY_ORDER_ID = "order";
20 |
21 |
22 | public LimitOrderCancelOperation(LimitOrder order, UserAccount feePayingAccount) {
23 | super(OperationType.LIMIT_ORDER_CANCEL_OPERATION);
24 | this.order = order;
25 | this.feePayingAccount = feePayingAccount;
26 | }
27 |
28 | // Inner fields of a limit order cancel operation
29 | private AssetAmount fee;
30 | private UserAccount feePayingAccount;
31 | private LimitOrder order;
32 |
33 | @Override
34 | public String toJsonString() {
35 | return null;
36 | }
37 |
38 | @Override
39 | public JsonElement toJsonObject() {
40 | JsonArray array = (JsonArray) super.toJsonObject();
41 | JsonObject jsonObject = new JsonObject();
42 | if(fee != null)
43 | jsonObject.add(KEY_FEE, fee.toJsonObject());
44 | jsonObject.addProperty(KEY_FEE_PAYING_ACCOUNT, feePayingAccount.getObjectId());
45 | jsonObject.addProperty(KEY_ORDER_ID, order.getObjectId());
46 | jsonObject.add(KEY_EXTENSIONS, new JsonArray());
47 | array.add(jsonObject);
48 | return array;
49 | }
50 |
51 | @Override
52 | public void setFee(AssetAmount assetAmount) {
53 | this.fee = assetAmount;
54 | }
55 |
56 | @Override
57 | public byte[] toBytes() {
58 | byte[] feeBytes = this.fee.toBytes();
59 | byte[] feePayingAccountBytes = this.feePayingAccount.toBytes();
60 | byte[] orderIdBytes = this.order.toBytes();
61 | byte[] extensions = this.extensions.toBytes();
62 | return Bytes.concat(feeBytes, feePayingAccountBytes, orderIdBytes, extensions);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/TransferOperation.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.google.common.primitives.Bytes;
4 | import com.google.gson.*;
5 | import com.gxchain.client.graphenej.enums.OperationType;
6 | import com.gxchain.client.graphenej.objects.AssetAmount;
7 | import com.gxchain.client.graphenej.objects.Memo;
8 | import com.gxchain.client.graphenej.objects.UserAccount;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | /**
13 | * Class used to encapsulate the TransferOperation operation related functionalities.
14 | */
15 | public class TransferOperation extends BaseOperation {
16 | public static final String KEY_AMOUNT = "amount";
17 | public static final String KEY_FROM = "from";
18 | public static final String KEY_TO = "to";
19 | public static final String KEY_MEMO = "memo";
20 |
21 | private AssetAmount fee;
22 | private AssetAmount amount;
23 | private UserAccount from;
24 | private UserAccount to;
25 | private Memo memo;
26 |
27 | public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount, AssetAmount fee) {
28 | super(OperationType.TRANSFER_OPERATION);
29 | this.from = from;
30 | this.to = to;
31 | this.amount = transferAmount;
32 | this.fee = fee;
33 | this.memo = new Memo();
34 | }
35 |
36 | public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount) {
37 | super(OperationType.TRANSFER_OPERATION);
38 | this.from = from;
39 | this.to = to;
40 | this.amount = transferAmount;
41 | this.memo = new Memo();
42 | }
43 |
44 | public TransferOperation(AssetAmount fee, AssetAmount amount, UserAccount from, UserAccount to, Memo memo) {
45 | super(OperationType.TRANSFER_OPERATION);
46 | this.fee = fee;
47 | this.amount = amount;
48 | this.from = from;
49 | this.to = to;
50 | this.memo = memo;
51 | }
52 |
53 | public UserAccount getFrom() {
54 | return this.from;
55 | }
56 |
57 | public UserAccount getTo() {
58 | return this.to;
59 | }
60 |
61 | public AssetAmount getAssetAmount() {
62 | return this.amount;
63 | }
64 |
65 | public AssetAmount getFee() {
66 | return this.fee;
67 | }
68 |
69 | public void setAssetAmount(AssetAmount assetAmount) {
70 | this.amount = assetAmount;
71 | }
72 |
73 | public void setFrom(UserAccount from) {
74 | this.from = from;
75 | }
76 |
77 | public void setTo(UserAccount to) {
78 | this.to = to;
79 | }
80 |
81 | public void setMemo(Memo memo) {
82 | this.memo = memo;
83 | }
84 |
85 | public Memo getMemo() {
86 | return this.memo;
87 | }
88 |
89 | @Override
90 | public void setFee(AssetAmount newFee) {
91 | this.fee = newFee;
92 | }
93 |
94 | @Override
95 | public byte[] toBytes() {
96 | byte[] feeBytes = fee.toBytes();
97 | byte[] fromBytes = from.toBytes();
98 | byte[] toBytes = to.toBytes();
99 | byte[] amountBytes = amount.toBytes();
100 | byte[] memoBytes = memo.toBytes();
101 | byte[] extensions = this.extensions.toBytes();
102 | return Bytes.concat(feeBytes, fromBytes, toBytes, amountBytes, memoBytes, extensions);
103 | }
104 |
105 | @Override
106 | public String toJsonString() {
107 | //TODO: Evaluate using simple Gson class to return a simple string representation and drop the TransferSerializer class
108 | GsonBuilder gsonBuilder = new GsonBuilder();
109 | gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferSerializer());
110 | return gsonBuilder.create().toJson(this);
111 | }
112 |
113 | @Override
114 | public JsonElement toJsonObject() {
115 | JsonArray array = new JsonArray();
116 | array.add(this.getId());
117 | JsonObject jsonObject = new JsonObject();
118 | if (fee != null)
119 | jsonObject.add(KEY_FEE, fee.toJsonObject());
120 | jsonObject.addProperty(KEY_FROM, from.getObjectId());
121 | jsonObject.addProperty(KEY_TO, to.getObjectId());
122 | jsonObject.add(KEY_AMOUNT, amount.toJsonObject());
123 | if (memo != null && memo.toJsonObject() != null) {
124 | jsonObject.add(KEY_MEMO, memo.toJsonObject());
125 | }
126 | jsonObject.add(KEY_EXTENSIONS, new JsonArray());
127 | array.add(jsonObject);
128 | return array;
129 | }
130 |
131 | public static class TransferSerializer implements JsonSerializer {
132 |
133 | @Override
134 | public JsonElement serialize(TransferOperation transfer, Type type, JsonSerializationContext jsonSerializationContext) {
135 | // JsonArray arrayRep = new JsonArray();
136 | // arrayRep.add(transfer.getId());
137 | // arrayRep.add(transfer.toJsonObject());
138 | // return arrayRep;
139 | return transfer.toJsonObject();
140 | }
141 | }
142 |
143 | /**
144 | * This deserializer will work on any transfer operation serialized in the 'array form' used a lot in
145 | * the Graphene Blockchain API.
146 | *
147 | * An example of this serialized form is the following:
148 | *
149 | * [
150 | * 0,
151 | * {
152 | * "fee": {
153 | * "amount": 264174,
154 | * "asset_id": "1.3.0"
155 | * },
156 | * "from": "1.2.138632",
157 | * "to": "1.2.129848",
158 | * "amount": {
159 | * "amount": 100,
160 | * "asset_id": "1.3.0"
161 | * },
162 | * "extensions": []
163 | * }
164 | * ]
165 | *
166 | * It will convert this data into a nice TransferOperation object.
167 | */
168 | public static class TransferDeserializer implements JsonDeserializer {
169 |
170 | @Override
171 | public TransferOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
172 | if (json.isJsonArray()) {
173 | // This block is used just to check if we are in the first step of the deserialization
174 | // when we are dealing with an array.
175 | JsonArray serializedTransfer = json.getAsJsonArray();
176 | if (serializedTransfer.get(0).getAsInt() != OperationType.TRANSFER_OPERATION.getCode()) {
177 | // If the operation type does not correspond to a transfer operation, we return null
178 | return null;
179 | } else {
180 | // Calling itself recursively, this is only done once, so there will be no problems.
181 | return context.deserialize(serializedTransfer.get(1), TransferOperation.class);
182 | }
183 | } else {
184 | // This block is called in the second recursion and takes care of deserializing the
185 | // transfer data itself.
186 | JsonObject jsonObject = json.getAsJsonObject();
187 |
188 | // Deserializing AssetAmount objects
189 | AssetAmount amount = context.deserialize(jsonObject.get(KEY_AMOUNT), AssetAmount.class);
190 | AssetAmount fee = context.deserialize(jsonObject.get(KEY_FEE), AssetAmount.class);
191 |
192 | // Deserializing UserAccount objects
193 | UserAccount from = new UserAccount(jsonObject.get(KEY_FROM).getAsString());
194 | UserAccount to = new UserAccount(jsonObject.get(KEY_TO).getAsString());
195 | TransferOperation transfer = new TransferOperation(from, to, amount, fee);
196 |
197 | // If the transfer had a memo, deserialize it
198 | if (jsonObject.has(KEY_MEMO)) {
199 | Memo memo = context.deserialize(jsonObject.get(KEY_MEMO), Memo.class);
200 | transfer.setMemo(memo);
201 | }
202 |
203 | return transfer;
204 | }
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/operations/TransferOperationBuilder.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.operations;
2 |
3 | import com.gxchain.client.graphenej.errors.MalformedOperationException;
4 | import com.gxchain.client.graphenej.objects.AssetAmount;
5 | import com.gxchain.client.graphenej.objects.Memo;
6 | import com.gxchain.client.graphenej.objects.UserAccount;
7 |
8 | /**
9 | * Factory class used to build a transfer operation
10 | */
11 | public class TransferOperationBuilder extends BaseOperationBuilder {
12 | private UserAccount from;
13 | private UserAccount to;
14 | private AssetAmount transferAmount;
15 | private AssetAmount fee;
16 | private Memo memo;
17 |
18 | public TransferOperationBuilder setSource(UserAccount from) {
19 | this.from = from;
20 | return this;
21 | }
22 |
23 | public TransferOperationBuilder setDestination(UserAccount to) {
24 | this.to = to;
25 | return this;
26 | }
27 |
28 | public TransferOperationBuilder setTransferAmount(AssetAmount transferAmount) {
29 | this.transferAmount = transferAmount;
30 | return this;
31 | }
32 |
33 | public TransferOperationBuilder setFee(AssetAmount fee) {
34 | this.fee = fee;
35 | return this;
36 | }
37 |
38 | public TransferOperationBuilder setMemo(Memo memo) {
39 | this.memo = memo;
40 | return this;
41 | }
42 |
43 | @Override
44 | public TransferOperation build(){
45 | TransferOperation transferOperation;
46 | if(from == null ){
47 | throw new MalformedOperationException("Missing source account information");
48 | }else if(to == null){
49 | throw new MalformedOperationException("Missing destination account information");
50 | }else if(transferAmount == null){
51 | throw new MalformedOperationException("Missing transfer amount information");
52 | }
53 | if(fee != null){
54 | transferOperation = new TransferOperation(from, to, transferAmount, fee);
55 | }else{
56 | transferOperation = new TransferOperation(from, to, transferAmount);
57 | }
58 | if(memo != null){
59 | transferOperation.setMemo(this.memo);
60 | }
61 | return transferOperation;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/graphenej/test/NaiveSSLContext.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.graphenej.test;
2 |
3 | /*
4 | * Copyright (C) 2015 Neo Visionaries Inc.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15 | * either express or implied. See the License for the specific
16 | * language governing permissions and limitations under the
17 | * License.
18 | */
19 | import javax.net.ssl.SSLContext;
20 | import javax.net.ssl.TrustManager;
21 | import javax.net.ssl.X509TrustManager;
22 | import java.security.KeyManagementException;
23 | import java.security.NoSuchAlgorithmException;
24 | import java.security.NoSuchProviderException;
25 | import java.security.Provider;
26 | import java.security.cert.X509Certificate;
27 |
28 |
29 | /**
30 | * A factory class which creates an {@link SSLContext} that
31 | * naively accepts all certificates without verification.
32 | *
33 | *
34 | * // Create an SSL context that naively accepts all certificates.
35 | * SSLContext context = NaiveSSLContext.getInstance("TLS");
36 | *
37 | * // Create a socket factory from the SSL context.
38 | * SSLSocketFactory factory = context.getSocketFactory();
39 | *
40 | * // Create a socket from the socket factory.
41 | * SSLSocket socket = factory.createSocket("www.example.com", 443);
42 | *
43 | *
44 | * @author Takahiko Kawasaki
45 | */
46 | public class NaiveSSLContext
47 | {
48 | private NaiveSSLContext()
49 | {
50 | }
51 |
52 |
53 | /**
54 | * Get an SSLContext that implements the specified secure
55 | * socket protocol and naively accepts all certificates
56 | * without verification.
57 | */
58 | public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException
59 | {
60 | return init(SSLContext.getInstance(protocol));
61 | }
62 |
63 |
64 | /**
65 | * Get an SSLContext that implements the specified secure
66 | * socket protocol and naively accepts all certificates
67 | * without verification.
68 | */
69 | public static SSLContext getInstance(String protocol, Provider provider) throws NoSuchAlgorithmException
70 | {
71 | return init(SSLContext.getInstance(protocol, provider));
72 | }
73 |
74 |
75 | /**
76 | * Get an SSLContext that implements the specified secure
77 | * socket protocol and naively accepts all certificates
78 | * without verification.
79 | */
80 | public static SSLContext getInstance(String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException
81 | {
82 | return init(SSLContext.getInstance(protocol, provider));
83 | }
84 |
85 |
86 | /**
87 | * Set NaiveTrustManager to the given context.
88 | */
89 | private static SSLContext init(SSLContext context)
90 | {
91 | try
92 | {
93 | // Set NaiveTrustManager.
94 | context.init(null, new TrustManager[] { new NaiveTrustManager() }, null);
95 | }
96 | catch (KeyManagementException e)
97 | {
98 | throw new RuntimeException("Failed to initialize an SSLContext.", e);
99 | }
100 |
101 | return context;
102 | }
103 |
104 |
105 | /**
106 | * A {@link TrustManager} which trusts all certificates naively.
107 | */
108 | private static class NaiveTrustManager implements X509TrustManager
109 | {
110 | @Override
111 | public X509Certificate[] getAcceptedIssuers()
112 | {
113 | return null;
114 | }
115 |
116 |
117 | public void checkClientTrusted(X509Certificate[] certs, String authType)
118 | {
119 | }
120 |
121 |
122 | public void checkServerTrusted(X509Certificate[] certs, String authType)
123 | {
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/GXChainApiRestClient.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.rpc;
2 |
3 | import com.google.gson.JsonArray;
4 | import com.google.gson.JsonElement;
5 | import com.google.gson.JsonObject;
6 | import com.gxchain.client.domian.params.GetTableRowsParams;
7 | import com.gxchain.client.graphenej.models.AccountProperties;
8 | import com.gxchain.client.graphenej.models.Block;
9 | import com.gxchain.client.graphenej.models.DynamicGlobalProperties;
10 | import com.gxchain.client.graphenej.models.contract.Abi;
11 | import com.gxchain.client.graphenej.models.contract.ContractAccountProperties;
12 | import com.gxchain.client.graphenej.models.contract.Table;
13 | import com.gxchain.client.graphenej.objects.Asset;
14 | import com.gxchain.client.graphenej.objects.AssetAmount;
15 | import com.gxchain.client.graphenej.operations.BaseOperation;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * @author liruobin
21 | * @since 2018/7/5 上午10:39
22 | */
23 | public interface GXChainApiRestClient {
24 |
25 | //////////////////////
26 | /// chain api
27 | /////////////////////
28 | /**
29 | * 链上查询
30 | *
31 | * @param method 方法名
32 | * @param params 参数
33 | * @return JsonElement
34 | */
35 | JsonElement query(String method, JsonArray params);
36 |
37 | /**
38 | * 查询gxchain chainId
39 | *
40 | * @return
41 | */
42 | String getChainId();
43 |
44 | /**
45 | * 查询全局动态参数
46 | *
47 | * @return
48 | */
49 | DynamicGlobalProperties getDynamicGlobalProperties();
50 |
51 | /**
52 | * 根据区块高度获取区块信息
53 | *
54 | * @param blockHeight 区块高度
55 | * @return
56 | */
57 | Block getBlock(long blockHeight);
58 |
59 | /**
60 | * 查询oject
61 | * 1.2.* 账号
62 | * 1.3.* 资产
63 | * 2.1.0 最新区块
64 | *
65 | * @param objectIds
66 | * @return
67 | */
68 | JsonElement getObjects(List objectIds);
69 |
70 | ///////////////////
71 | ////account api
72 | //////////////////
73 |
74 | /**
75 | * 查询账户余额
76 | *
77 | * @param accountId 账户id
78 | * @param assetIds 资产id list
79 | * @return
80 | */
81 | List getAccountBalances(String accountId, List assetIds);
82 |
83 | /**
84 | * 根据名称获取公链账户信息
85 | *
86 | * @param accountName
87 | * @return
88 | */
89 | AccountProperties getAccountByName(String accountName);
90 |
91 | /**
92 | * 根据名称获取智能合约账户信息
93 | * @param accountName
94 | * @return
95 | */
96 | ContractAccountProperties getContractAccountByName(String accountName);
97 |
98 | /**
99 | * 根据公钥查询账户id
100 | * @param publicKey 公钥
101 | * @return 账户id列表
102 | */
103 | List getAccountByPublicKey(String publicKey);
104 |
105 | /**
106 | * 根据accountId查询公链账户信息
107 | *
108 | * @param accountIds
109 | * @return
110 | */
111 | List getAccounts(List accountIds);
112 |
113 | /**
114 | * 获取交易费率
115 | *
116 | * @param operations 交易操作
117 | * @param feeAsset 费用资产
118 | */
119 | List getRequiredFees(List operations, Asset feeAsset);
120 |
121 | /**
122 | * 根据资产标识获取资产信息
123 | * @param symbols [GXC]
124 | * @return
125 | */
126 | List getAssets(List symbols);
127 |
128 | /**
129 | * get contract abi by contract_name
130 | * @param contractName
131 | * @return
132 | */
133 | Abi getContractABI(String contractName);
134 |
135 | /**
136 | * get contract table by contract_name
137 | * @param contractName
138 | * @return
139 | */
140 | List getContractTable(String contractName);
141 |
142 | /**
143 | * 查询智能合约表数据
144 | * @param contractName 合约名称
145 | * @param tableName 表名
146 | * @param lowerBound 查询时指定的key最小值, 默认为0
147 | * @param upperBound 查询时指定的key最大值,默认为-1,即最大的无符号整形
148 | * @return
149 | */
150 | JsonElement getTableRows(String contractName, String tableName, Number lowerBound, Number upperBound);
151 |
152 | /**
153 | * 查询智能合约表数据(扩展)
154 | * @param contractName 合约名称
155 | * @param tableName 表名
156 | * @param getTableRowsParams 查询扩展字段
157 | * @return
158 | */
159 | JsonElement getTableRowsEx(String contractName, String tableName, GetTableRowsParams getTableRowsParams);
160 |
161 | /**
162 | * 广播交易
163 | * 广播成功后立即返回,耗时短
164 | * @param transaction
165 | * @return
166 | */
167 | JsonElement broadcast(JsonObject transaction);
168 |
169 | /**
170 | * 广播交易
171 | * 打包成功后返回,不过被打包的也有可能被回滚,耗时长
172 | * @param transaction
173 | * @return
174 | */
175 | JsonElement broadcastSynchronous(JsonObject transaction);
176 | }
177 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/GXChainClientFactory.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.rpc;
2 |
3 |
4 | import com.gxchain.client.rpc.impl.GXChainApiRestClientImpl;
5 |
6 | /**
7 | * gxchain client 工厂类
8 | *
9 | * @author liruobin
10 | * @since 2018/7/3 上午10:15
11 | */
12 | public class GXChainClientFactory {
13 | private static GXChainClientFactory clientFactory = new GXChainClientFactory();
14 |
15 | public static GXChainClientFactory getInstance() {
16 | return clientFactory;
17 | }
18 | /**
19 | * 创建http client
20 | * @param url
21 | * @return
22 | */
23 | public GXChainApiRestClient newRestClient(String url) {
24 | return new GXChainApiRestClientImpl(url);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/api/GXChainApiService.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.rpc.api;
2 |
3 | import com.google.gson.JsonElement;
4 | import com.gxchain.client.graphenej.models.ApiCall;
5 | import com.gxchain.client.graphenej.models.WitnessResponse;
6 | import retrofit2.Call;
7 | import retrofit2.http.Body;
8 | import retrofit2.http.POST;
9 |
10 | /**
11 | * @author liruobin
12 | * @since 2018/7/5 上午10:33
13 | */
14 | public interface GXChainApiService {
15 | @POST("/rpc")
16 | Call> call(@Body ApiCall apiCall);
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/api/GxbApiFactory.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.rpc.api;
2 |
3 |
4 | import com.gxchain.client.exception.HttpAccessFailException;
5 | import okhttp3.HttpUrl;
6 | import okhttp3.Interceptor;
7 | import okhttp3.OkHttpClient;
8 | import okhttp3.Response;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import retrofit2.Retrofit;
12 |
13 | import java.io.IOException;
14 | import java.util.concurrent.ConcurrentHashMap;
15 | import java.util.concurrent.TimeUnit;
16 |
17 |
18 | public class GxbApiFactory {
19 | private static final Logger logger = LoggerFactory.getLogger(GxbApiFactory.class);
20 | private ConcurrentHashMap, Object> typeCache = new ConcurrentHashMap<>();
21 |
22 | private Retrofit retrofit;
23 |
24 | private GxbApiFactory(final String baseUrl, final Long timeout) {
25 | OkHttpClient httpClient = new OkHttpClient().newBuilder().readTimeout(timeout, TimeUnit.MILLISECONDS).addInterceptor(new Interceptor() {
26 |
27 | /*
28 | * 记录访问日志,统一接口异常处理
29 | *
30 | * @see okhttp3.Interceptor#intercept(okhttp3.Interceptor.Chain)
31 | */
32 | @Override
33 | public Response intercept(Chain chain) throws IOException {
34 | logger.info("gxb api request:" + chain.request().toString());
35 | long l1 = System.currentTimeMillis();
36 | Response response = chain.proceed(chain.request());
37 | logger.info("gxb response," + (System.currentTimeMillis() - l1) + "ms," + response.toString());
38 | if (!response.isSuccessful()) {
39 | throw new HttpAccessFailException(response.body().string());
40 | } else {
41 | return response;
42 | }
43 | }
44 |
45 | }).writeTimeout(timeout, TimeUnit.MILLISECONDS).build();
46 | httpClient.dispatcher().setMaxRequestsPerHost(100);
47 | httpClient.dispatcher().setMaxRequests(150);
48 |
49 | retrofit = new Retrofit.Builder().baseUrl(HttpUrl.parse(baseUrl)).addConverterFactory(GxbGsonConverterFactory.create()).callFactory(httpClient).build();
50 | }
51 |
52 |
53 | public T newApi(Class clz) {
54 | Object object = typeCache.get(clz);
55 | if (object != null) {
56 | return (T) object;
57 | } else {
58 | if (clz.isInterface()) {
59 | T result = retrofit.create(clz);
60 | typeCache.putIfAbsent(clz, result);
61 | return result;
62 | } else {
63 | throw new IllegalArgumentException("interface class required");
64 | }
65 | }
66 | }
67 |
68 | public static Builder builder() {
69 | return new Builder();
70 | }
71 |
72 | public static class Builder {
73 | private String baseUrl;
74 | private Long timeout = 15000L; // default 15s;
75 |
76 | public Builder baseUrl(String url) {
77 | this.baseUrl = url;
78 | return this;
79 | }
80 |
81 | public Builder timeout(Long ms) {
82 | this.timeout = ms;
83 | return this;
84 | }
85 |
86 | public GxbApiFactory build() {
87 | if (baseUrl == null || baseUrl.isEmpty()) {
88 | throw new IllegalArgumentException("baseUrl invalid");
89 | }
90 | return new GxbApiFactory(this.baseUrl, timeout);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/api/GxbGsonConverterFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The
3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any
4 | * incorporation of the same into any other material in any media or format of any kind is strictly
5 | * prohibited. All rights are reserved.
6 | *
7 | * Copyright (c) gxb.io Inc. 2017
8 | */
9 | package com.gxchain.client.rpc.api;
10 |
11 | import com.google.gson.Gson;
12 | import com.google.gson.TypeAdapter;
13 | import com.google.gson.reflect.TypeToken;
14 | import com.gxchain.client.util.GXGsonUtil;
15 | import okhttp3.RequestBody;
16 | import okhttp3.ResponseBody;
17 | import retrofit2.Converter;
18 | import retrofit2.Retrofit;
19 |
20 | import java.lang.annotation.Annotation;
21 | import java.lang.reflect.Type;
22 |
23 | public class GxbGsonConverterFactory extends Converter.Factory {
24 |
25 | private final Gson gson;
26 |
27 | private GxbGsonConverterFactory(Gson gson) {
28 | if (gson == null) throw new NullPointerException("gson == null");
29 | this.gson = gson;
30 | }
31 |
32 | public static GxbGsonConverterFactory create() {
33 | return create(GXGsonUtil.getGson());
34 | }
35 |
36 | public static GxbGsonConverterFactory create(Gson gson) {
37 | return new GxbGsonConverterFactory(gson);
38 | }
39 |
40 | @Override
41 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
42 | TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
43 | return new GxbGsonResponseBodyConverter(gson, adapter);
44 | }
45 |
46 | @Override
47 | public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations,
48 | Retrofit retrofit) {
49 | TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
50 | return new GxbGsonRequestBodyConverter(gson, adapter);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/api/GxbGsonRequestBodyConverter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The
3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any
4 | * incorporation of the same into any other material in any media or format of any kind is strictly
5 | * prohibited. All rights are reserved.
6 | *
7 | * Copyright (c) gxb.io Inc. 2017
8 | */
9 | package com.gxchain.client.rpc.api;
10 |
11 | import com.google.gson.Gson;
12 | import com.google.gson.TypeAdapter;
13 | import com.google.gson.stream.JsonWriter;
14 | import okhttp3.MediaType;
15 | import okhttp3.RequestBody;
16 | import okio.Buffer;
17 | import okio.ByteString;
18 | import org.slf4j.Logger;
19 | import org.slf4j.LoggerFactory;
20 | import retrofit2.Converter;
21 |
22 | import java.io.IOException;
23 | import java.io.OutputStreamWriter;
24 | import java.io.Writer;
25 | import java.nio.charset.Charset;
26 |
27 | public class GxbGsonRequestBodyConverter implements Converter {
28 | private static final Logger logger = LoggerFactory.getLogger(GxbGsonRequestBodyConverter.class);
29 |
30 | private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
31 | private static final Charset UTF_8 = Charset.forName("UTF-8");
32 |
33 | private final Gson gson;
34 | private final TypeAdapter adapter;
35 |
36 | GxbGsonRequestBodyConverter(Gson gson, TypeAdapter adapter) {
37 | this.gson = gson;
38 | this.adapter = adapter;
39 | }
40 |
41 | @Override
42 | public RequestBody convert(T value) throws IOException {
43 | Buffer buffer = new Buffer();
44 | Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
45 | JsonWriter jsonWriter = gson.newJsonWriter(writer);
46 | adapter.write(jsonWriter, value);
47 | jsonWriter.close();
48 | ByteString requestBody = buffer.readByteString();
49 | logger.debug("get GxbGsonRequestBody: {}", requestBody.string(UTF_8));
50 |
51 | return RequestBody.create(MEDIA_TYPE, requestBody);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/rpc/api/GxbGsonResponseBodyConverter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The
3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any
4 | * incorporation of the same into any other material in any media or format of any kind is strictly
5 | * prohibited. All rights are reserved.
6 | *
7 | * Copyright (c) gxb.io Inc. 2017
8 | */
9 | package com.gxchain.client.rpc.api;
10 |
11 | import com.google.gson.Gson;
12 | import com.google.gson.TypeAdapter;
13 | import com.google.gson.stream.JsonReader;
14 | import okhttp3.MediaType;
15 | import okhttp3.ResponseBody;
16 | import org.slf4j.Logger;
17 | import org.slf4j.LoggerFactory;
18 | import retrofit2.Converter;
19 |
20 | import java.io.*;
21 | import java.nio.charset.Charset;
22 |
23 |
24 | public class GxbGsonResponseBodyConverter implements Converter {
25 | private static final Logger logger = LoggerFactory.getLogger(GxbGsonResponseBodyConverter.class);
26 | private final Gson gson;
27 | private final TypeAdapter adapter;
28 |
29 | GxbGsonResponseBodyConverter(Gson gson, TypeAdapter adapter) {
30 | this.gson = gson;
31 | this.adapter = adapter;
32 | }
33 |
34 | @Override
35 | public T convert(ResponseBody value) throws IOException {
36 | String response = value.string();
37 | logger.debug("get GxbGsonResponseBody: {}", response);
38 |
39 | MediaType contentType = value.contentType();
40 | Charset charset = contentType != null ? contentType.charset(Charset.forName("UTF-8")) : Charset.forName("UTF-8");
41 | InputStream inputStream = new ByteArrayInputStream(response.getBytes());
42 | Reader reader = new InputStreamReader(inputStream, charset);
43 | JsonReader jsonReader = gson.newJsonReader(reader);
44 | try {
45 | return adapter.read(jsonReader);
46 | } finally {
47 | value.close();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/util/BeanUtils.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.util;
2 |
3 | import net.sf.cglib.beans.BeanCopier;
4 |
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | public final class BeanUtils {
9 | private BeanUtils() {
10 |
11 | }
12 |
13 | private static final Map beanCopierMap = new ConcurrentHashMap<>();
14 |
15 | /**
16 | * 基于CGLIB的bean properties 的拷贝,性能要远优于{@code org.springframework.beans.BeanUtils.copyProperties}
17 | *
18 | * @param source
19 | * @param target
20 | */
21 | public static void copyProperties(Object source, Object target) {
22 | if (source == null || target == null) {
23 | target = null;
24 | return;
25 | }
26 |
27 | String key = String.format("%s:%s", source.getClass().getName(), target.getClass().getName());
28 | if (!beanCopierMap.containsKey(key)) {
29 | BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
30 | beanCopierMap.putIfAbsent(key, beanCopier);
31 | }
32 | BeanCopier beanCopier = beanCopierMap.get(key);
33 | beanCopier.copy(source, target, null);
34 | }
35 | }
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/util/GXGsonUtil.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.util;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.gxchain.client.graphenej.models.ApiCall;
6 | import com.gxchain.client.graphenej.models.DynamicGlobalProperties;
7 | import com.gxchain.client.graphenej.objects.*;
8 | import com.gxchain.client.graphenej.operations.AccountCreateOperation;
9 | import com.gxchain.client.graphenej.operations.TransferOperation;
10 |
11 | import java.lang.reflect.Type;
12 |
13 | /**
14 | * @author liruobin
15 | * @since 2018/7/3 下午4:48
16 | */
17 | public class GXGsonUtil {
18 | private static GsonBuilder builder = new GsonBuilder();
19 |
20 | static {
21 | builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer());
22 | builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
23 | builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
24 | builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
25 | builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
26 | builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer());
27 | builder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer());
28 | builder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer());
29 | builder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer());
30 | builder.registerTypeAdapter(ApiCall.class, new ApiCall.ApiCallSerializer());
31 | builder.registerTypeAdapter(AccountCreateOperation.class, new AccountCreateOperation.AccountCreateDeserializer());
32 | }
33 |
34 | public static Gson getGson() {
35 | return builder.create();
36 | }
37 |
38 | public static T fromJson(String json, Type typeOfT) {
39 | return getGson().fromJson(json, typeOfT);
40 | }
41 |
42 | public static T fromJson(String json, Class className) {
43 | return getGson().fromJson(json, className);
44 | }
45 |
46 | public static String toJson(Object src) {
47 | return getGson().toJson(src);
48 | }
49 |
50 | public static T mapJson(Object src, Class className) {
51 | return fromJson(toJson(src), className);
52 | }
53 |
54 | public static T mapJson(Object src, Type typeOfT) {
55 | return fromJson(toJson(src), typeOfT);
56 | }
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/gxchain/client/util/TxSerializerUtil.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.util;
2 |
3 | import com.google.gson.JsonElement;
4 | import com.google.gson.JsonObject;
5 | import com.gxchain.client.exception.GXChainApiException;
6 | import com.gxchain.common.signature.utils.Util;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import javax.script.ScriptEngine;
10 | import javax.script.ScriptEngineManager;
11 | import java.io.*;
12 |
13 | /**
14 | * @author liruobin
15 | * @since 2019/2/14 5:27 PM
16 | */
17 | @Slf4j
18 | public class TxSerializerUtil {
19 | private static ScriptEngine engine;
20 |
21 | static {
22 | log.info("init script engine");
23 | ScriptEngineManager manager = new ScriptEngineManager();
24 | engine = manager.getEngineByName("nashorn");
25 | try {
26 | InputStream inputStream = TxSerializerUtil.class.getClassLoader().getResourceAsStream("js/tx_serializer.min.js");
27 | StringBuilder script = new StringBuilder();
28 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
29 | String line = "";
30 | while ((line = br.readLine()) != null) {
31 | script.append(line);
32 | }
33 | engine.eval(script.toString());
34 | } catch (Exception e) {
35 | log.error(e.getMessage(), e);
36 | throw new GXChainApiException(e.getMessage());
37 | }
38 | log.info("init script engine finish");
39 | }
40 |
41 | public static byte[] serializeTransaction(JsonObject transaction) {
42 | try {
43 | log.debug("start serialize");
44 | String result = (String) engine.eval("serializer.serializeTransaction(" + transaction.toString() + ").toString('hex')");
45 | log.debug("serialize finished:{}", result);
46 | return Util.hexToBytes(result);
47 | } catch (Exception e) {
48 | log.error(e.getMessage(), e);
49 | throw new GXChainApiException(e.getMessage());
50 | }
51 | }
52 |
53 | public static String serializeCallData(String methodName, JsonElement param, JsonElement abi) {
54 | try {
55 | String script = String.format("serializer.serializeCallData(\"%s\",%s,%s).toString('hex')", methodName, param == null ? new JsonObject().toString() : param.toString(), abi.toString());
56 | return (String) engine.eval(script);
57 | } catch (Exception e) {
58 | log.error(e.getMessage(), e);
59 | throw new GXChainApiException(e.getMessage());
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/com/gxchain/client/util/GXGsonUtilTest.java:
--------------------------------------------------------------------------------
1 | package com.gxchain.client.util;
2 |
3 | import com.gxchain.client.graphenej.objects.Transaction;
4 | import org.junit.Test;
5 |
6 | /**
7 | * @author liruobin
8 | * @since 2019/6/4 9:31 PM
9 | */
10 | public class GXGsonUtilTest {
11 | @Test
12 | public void transferDeserializerTest() {
13 | String hex = "{\"ref_block_num\": 33185,\n" +
14 | " \"ref_block_prefix\": 1487534235,\n" +
15 | " \"expiration\": \"2019-05-23T11:34:15\",\n" +
16 | " \"operations\": [\n" +
17 | " [\n" +
18 | " 0,\n" +
19 | " {\n" +
20 | " \"fee\": {\n" +
21 | " \"amount\": 1000,\n" +
22 | " \"asset_id\": \"1.3.1\"\n" +
23 | " },\n" +
24 | " \"from\": \"1.2.521\",\n" +
25 | " \"to\": \"1.2.2136\",\n" +
26 | " \"amount\": {\n" +
27 | " \"amount\": 110000,\n" +
28 | " \"asset_id\": \"1.3.1\"\n" +
29 | " },\n" +
30 | " \"extensions\": []\n" +
31 | " }\n" +
32 | " ]\n" +
33 | " ],\n" +
34 | " \"extensions\": [],\n" +
35 | " \"signatures\": [\n" +
36 | " \"1f56095fd3ed01eea62d609c01d8f00160b42e5d12de7d0b9538bce63b80b02a53504cd67b152cca1348f8678afdbaeee32461d6946d1bb4ef83d2cf4a03e1b539\"\n" +
37 | " ]}";
38 | Transaction transaction = GXGsonUtil.fromJson(hex, Transaction.class);
39 | transaction.setChainId("4f7d07969c446f8342033acb3ab2ae5044cbe0fde93db02de75bd17fa8fd84b8");
40 |
41 | System.out.println(transaction.toJsonObjectNoSign());
42 | }
43 |
44 | @Test
45 | public void createAccountDeserializerTest() {
46 | String hex = "{\"ref_block_num\": 25100,\n" +
47 | " \"ref_block_prefix\": 2370734044,\n" +
48 | " \"expiration\": \"2019-06-05T02:55:12\",\n" +
49 | " \"operations\": [\n" +
50 | " [\n" +
51 | " 5,\n" +
52 | " {\n" +
53 | " \"fee\": {\n" +
54 | " \"amount\": 102,\n" +
55 | " \"asset_id\": \"1.3.1\"\n" +
56 | " },\n" +
57 | " \"registrar\": \"1.2.26\",\n" +
58 | " \"referrer\": \"1.2.26\",\n" +
59 | " \"referrer_percent\": 0,\n" +
60 | " \"name\": \"w13920644712\",\n" +
61 | " \"owner\": {\n" +
62 | " \"weight_threshold\": 1,\n" +
63 | " \"account_auths\": [],\n" +
64 | " \"key_auths\": [\n" +
65 | " [\n" +
66 | " \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" +
67 | " 1\n" +
68 | " ]\n" +
69 | " ],\n" +
70 | " \"address_auths\": []\n" +
71 | " },\n" +
72 | " \"active\": {\n" +
73 | " \"weight_threshold\": 1,\n" +
74 | " \"account_auths\": [],\n" +
75 | " \"key_auths\": [\n" +
76 | " [\n" +
77 | " \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" +
78 | " 1\n" +
79 | " ]\n" +
80 | " ],\n" +
81 | " \"address_auths\": []\n" +
82 | " },\n" +
83 | " \"options\": {\n" +
84 | " \"memo_key\": \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" +
85 | " \"voting_account\": \"1.2.5\",\n" +
86 | " \"num_witness\": 0,\n" +
87 | " \"num_committee\": 0,\n" +
88 | " \"votes\": [],\n" +
89 | " \"extensions\": []\n" +
90 | " },\n" +
91 | " \"extensions\": {}\n" +
92 | " }\n" +
93 | " ]\n" +
94 | " ],\n" +
95 | " \"extensions\": [],\n" +
96 | " \"signatures\": [\n" +
97 | " \"2029cab05cc3d032ee402092c4b6ccc7b20f6380aab2dca4763448d50e69e3581b7494ed1f92a9da8e7a0053766e7cb112f8aff324d7e6e86f9be78d374319c377\"\n" +
98 | " ]}";
99 | Transaction transaction = GXGsonUtil.fromJson(hex, Transaction.class);
100 | transaction.setChainId("4f7d07969c446f8342033acb3ab2ae5044cbe0fde93db02de75bd17fa8fd84b8");
101 | transaction.setPrivateKey("5J8CYVMcMz2f7vDc9fYcySbVUx7f3JnCJJVBeE6eWJmwZToTSqz");
102 | System.out.println(transaction.toJsonObject());
103 | }
104 | }
--------------------------------------------------------------------------------