assetDefinition;
123 |
124 | /**
125 | * The id of the asset being controlled.
126 | */
127 | @SerializedName("asset_id")
128 | public String assetId;
129 |
130 | /**
131 | * The control program which must be satisfied to transfer this output.
132 | */
133 | @SerializedName("control_program")
134 | private String controlProgram;
135 |
136 | /**
137 | * The id of the output.
138 | */
139 | @SerializedName("id")
140 | private String id;
141 |
142 | /**
143 | * The output's position in a transaction's list of outputs.
144 | */
145 | private Integer position;
146 |
147 | /**
148 | * The type the output.
149 | * Possible values are "control" and "retire".
150 | */
151 | private String type;
152 |
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/api/Receiver.java:
--------------------------------------------------------------------------------
1 | package io.bytom.api;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 | import io.bytom.common.Utils;
5 | import io.bytom.exception.JSONException;
6 |
7 | /**
8 | * Receivers are used to facilitate payments between accounts on different
9 | * cores. They contain a control program and an expiration date. In the future,
10 | * more payment-related metadata may be placed here.
11 | *
12 | * Receivers are typically created under accounts via the
13 | * {@link Account.ReceiverBuilder} class.
14 | */
15 | public class Receiver {
16 |
17 | @SerializedName("address")
18 | public String address;
19 | /**
20 | * Hex-encoded string representation of the control program.
21 | */
22 | @SerializedName("control_program")
23 | public String controlProgram;
24 |
25 |
26 | /**
27 | * Serializes the receiver into a form that is safe to transfer over the wire.
28 | *
29 | * @return the JSON-serialized representation of the Receiver object
30 | */
31 | public String toJson() {
32 | return Utils.serializer.toJson(this);
33 | }
34 |
35 | /**
36 | * Deserializes a Receiver from JSON.
37 | *
38 | * @param json a JSON-serialized Receiver object
39 | * @return the deserialized Receiver object
40 | * @throws JSONException Raised if the provided string is not valid JSON.
41 | */
42 | public static Receiver fromJson(String json) throws JSONException {
43 | try {
44 | return Utils.serializer.fromJson(json, Receiver.class);
45 | } catch (IllegalStateException e) {
46 | throw new JSONException("Unable to parse serialized receiver: " + e.getMessage());
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/api/UnconfirmedTransaction.java:
--------------------------------------------------------------------------------
1 | package io.bytom.api;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 | import io.bytom.common.Utils;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.apache.log4j.Logger;
8 |
9 | import java.lang.reflect.Type;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class UnconfirmedTransaction {
15 | /**
16 | * Unique identifier, or transaction hash, of a transaction.
17 | */
18 | private String id;
19 |
20 | /**
21 | * version
22 | */
23 | private Integer version;
24 |
25 | /**
26 | * size
27 | */
28 | private Integer size;
29 | /**
30 | * time_range
31 | */
32 | @SerializedName("time_range")
33 | private Integer timeRange;
34 |
35 | /**
36 | * status
37 | */
38 | @SerializedName("status_fail")
39 | private boolean statusFail;
40 |
41 | /**
42 | * List of specified inputs for a transaction.
43 | */
44 | private List inputs;
45 |
46 | /**
47 | * List of specified outputs for a transaction.
48 | */
49 | private List outputs;
50 |
51 | private static Logger logger = Logger.getLogger(UnconfirmedTransaction.class);
52 |
53 | /**
54 | * Serializes the UnconfirmedTransaction into a form that is safe to transfer over the wire.
55 | *
56 | * @return the JSON-serialized representation of the UnconfirmedTransaction object
57 | */
58 | public String toJson() {
59 | return Utils.serializer.toJson(this);
60 | }
61 |
62 | /**
63 | * Call get-unconfirmed-transaction api
64 | *
65 | * @param client
66 | * @param txId
67 | * @return
68 | * @throws BytomException
69 | */
70 | public static UnconfirmedTransaction get(Client client, String txId) throws BytomException {
71 | Map req = new HashMap();
72 | req.put("tx_id", txId);
73 | UnconfirmedTransaction UCTX = client.request("get-unconfirmed-transaction",
74 | req, UnconfirmedTransaction.class);
75 |
76 | logger.info("get-unconfirmed-transaction:");
77 | logger.info(UCTX.toJson());
78 |
79 | return UCTX;
80 | }
81 |
82 | public static UTXResponse list(Client client) throws BytomException {
83 | UTXResponse utxResponse =
84 | client.request("list-unconfirmed-transactions", null, UTXResponse.class);
85 |
86 | logger.info("list-unconfirmed-transactions:");
87 | logger.info(utxResponse.toJson());
88 |
89 | return utxResponse;
90 | }
91 |
92 | public static class UTXResponse {
93 |
94 | @SerializedName("total")
95 | public Integer total;
96 |
97 | @SerializedName("tx_ids")
98 | public List txIds;
99 |
100 | public String toJson() {
101 | return Utils.serializer.toJson(this);
102 | }
103 | }
104 |
105 | public static class AnnotatedInput {
106 |
107 | /**
108 | * address
109 | */
110 | private String address;
111 |
112 | /**
113 | * The number of units of the asset being issued or spent.
114 | */
115 | private long amount;
116 |
117 | /**
118 | * The definition of the asset being issued or spent (possibly null).
119 | */
120 | @SerializedName("asset_definition")
121 | private Map assetDefinition;
122 |
123 | /**
124 | * The id of the asset being issued or spent.
125 | */
126 | @SerializedName("asset_id")
127 | private String assetId;
128 |
129 | /**
130 | * The control program which must be satisfied to transfer this output.
131 | */
132 | @SerializedName("control_program")
133 | private String controlProgram;
134 |
135 | /**
136 | * The id of the output consumed by this input. Null if the input is an
137 | * issuance.
138 | */
139 | @SerializedName("spent_output_id")
140 | private String spentOutputId;
141 |
142 | /**
143 | * The type of the input.
144 | * Possible values are "issue" and "spend".
145 | */
146 | private String type;
147 | }
148 |
149 | public static class AnnotatedOutput {
150 |
151 | /**
152 | * address
153 | */
154 | private String address;
155 |
156 | /**
157 | * The number of units of the asset being controlled.
158 | */
159 | private long amount;
160 |
161 | /**
162 | * The definition of the asset being controlled (possibly null).
163 | */
164 | @SerializedName("asset_definition")
165 | private Map assetDefinition;
166 |
167 | /**
168 | * The id of the asset being controlled.
169 | */
170 | @SerializedName("asset_id")
171 | public String assetId;
172 |
173 | /**
174 | * The control program which must be satisfied to transfer this output.
175 | */
176 | @SerializedName("control_program")
177 | private String controlProgram;
178 |
179 | /**
180 | * The id of the output.
181 | */
182 | @SerializedName("id")
183 | private String id;
184 |
185 | /**
186 | * The output's position in a transaction's list of outputs.
187 | */
188 | private Integer position;
189 |
190 | /**
191 | * The type the output.
192 | * Possible values are "control" and "retire".
193 | */
194 | private String type;
195 |
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/api/UnspentOutput.java:
--------------------------------------------------------------------------------
1 | package io.bytom.api;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 | import io.bytom.common.ParameterizedTypeImpl;
5 | import io.bytom.common.Utils;
6 | import io.bytom.exception.BytomException;
7 | import io.bytom.http.Client;
8 | import org.apache.log4j.Logger;
9 |
10 | import java.lang.reflect.Type;
11 | import java.util.List;
12 |
13 | public class UnspentOutput {
14 |
15 | /**
16 | * The id of the asset being controlled.
17 | */
18 | @SerializedName("asset_id")
19 | public String assetId;
20 |
21 | /**
22 | * The alias of the asset being controlled.
23 | */
24 | @SerializedName("asset_alias")
25 | public String assetAlias;
26 |
27 | /**
28 | * The number of units of the asset being controlled.
29 | */
30 | public long amount;
31 |
32 | /**
33 | * address of account
34 | */
35 | public String address;
36 |
37 | /**
38 | * whether the account address is change
39 | */
40 | public boolean change;
41 |
42 | /**
43 | * The ID of the output.
44 | */
45 | @SerializedName("id")
46 | public String id;
47 |
48 | /**
49 | * The control program which must be satisfied to transfer this output.
50 | */
51 | @SerializedName("program")
52 | public String program;
53 |
54 | @SerializedName("control_program_index")
55 | public String controlProgramIndex;
56 |
57 | /**
58 | * source unspent output id
59 | */
60 | @SerializedName("source_id")
61 | public String sourceId;
62 |
63 | /**
64 | * position of source unspent output id in block
65 | */
66 | @SerializedName("source_pos")
67 | public int sourcePos;
68 |
69 | /**
70 | * The definition of the asset being controlled (possibly null).
71 | */
72 | @SerializedName("valid_height")
73 | public int validHeight;
74 |
75 | private static Logger logger = Logger.getLogger(UnspentOutput.class);
76 |
77 | /**
78 | * Serializes the Address into a form that is safe to transfer over the wire.
79 | *
80 | * @return the JSON-serialized representation of the Receiver object
81 | */
82 | public String toJson() {
83 | return Utils.serializer.toJson(this);
84 | }
85 |
86 | public static class QueryBuilder {
87 |
88 | /**
89 | * id of unspent output.
90 | */
91 | public String id;
92 |
93 | /**
94 | * The id of the account controlling this output (possibly null if a control program
95 | * is specified).
96 | */
97 | @SerializedName("account_id")
98 | public String accountId;
99 |
100 | /**
101 | * The alias of the account controlling this output (possibly null if a control
102 | * program is specified).
103 | */
104 | @SerializedName("account_alias")
105 | public String accountAlias;
106 |
107 | public Boolean unconfirmed;
108 |
109 | @SerializedName("smart_contract")
110 | public Boolean smartContract;
111 |
112 | public Integer from;
113 |
114 | public Integer count;
115 |
116 | public QueryBuilder setId(String id) {
117 | this.id = id;
118 | return this;
119 | }
120 |
121 | public QueryBuilder setAccountId(String accountId) {
122 | this.accountId = accountId;
123 | return this;
124 | }
125 |
126 | public QueryBuilder setAccountAlias(String accountAlias) {
127 | this.accountAlias = accountAlias;
128 | return this;
129 | }
130 |
131 | public QueryBuilder setSmartContract(boolean smartContract) {
132 | this.smartContract = smartContract;
133 | return this;
134 | }
135 |
136 | public QueryBuilder setUnconfirmed(boolean unconfirmed) {
137 | this.unconfirmed = unconfirmed;
138 | return this;
139 | }
140 |
141 | public QueryBuilder setFrom(Integer from) {
142 | this.from = from;
143 | return this;
144 | }
145 |
146 | public QueryBuilder setCount(Integer count) {
147 | this.count = count;
148 | return this;
149 | }
150 |
151 | /**
152 | * call list-unspent-outputs api
153 | *
154 | * @param client client object that makes requests to the core
155 | * @return
156 | * @throws BytomException BytomException
157 | */
158 | public List list(Client client) throws BytomException {
159 |
160 | Type listType = new ParameterizedTypeImpl(List.class, new Class[]{UnspentOutput.class});
161 | List unspentOutputList = client.request("list-unspent-outputs", this, listType);
162 | logger.info("list-unspent-outputs:");
163 | logger.info("size of unspentOutputList:" + unspentOutputList.size());
164 | for (UnspentOutput UTXO : unspentOutputList) {
165 | logger.info(UTXO.toJson());
166 | }
167 |
168 | return unspentOutputList;
169 | }
170 |
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/api/Wallet.java:
--------------------------------------------------------------------------------
1 | package io.bytom.api;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 | import io.bytom.common.Utils;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.apache.log4j.Logger;
8 |
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | /**
14 | * Wallet Class
15 | */
16 | public class Wallet {
17 |
18 | @SerializedName("account_image")
19 | public AccountImage accountImage;
20 |
21 | @SerializedName("asset_image")
22 | public AssetImage assetImage;
23 |
24 | @SerializedName("key_images")
25 | public KeyImages keyImages;
26 |
27 | private static Logger logger = Logger.getLogger(Wallet.class);
28 |
29 | /**
30 | * Serializes the Address into a form that is safe to transfer over the wire.
31 | *
32 | * @return the JSON-serialized representation of the Receiver object
33 | */
34 | public String toJson() {
35 | return Utils.serializer.toJson(this);
36 | }
37 |
38 | /**
39 | * Call backup-wallet api
40 | *
41 | * @param client
42 | * @return
43 | * @throws BytomException
44 | */
45 | public static Wallet backupWallet(Client client) throws BytomException {
46 | Wallet wallet = client.request("backup-wallet", null, Wallet.class);
47 |
48 | logger.info("backup-wallet:");
49 | logger.info(wallet.toJson());
50 |
51 | return wallet;
52 | }
53 |
54 | /**
55 | * Call restore-wallet api
56 | *
57 | * @param client
58 | * @param accountImage
59 | * @param assetImage
60 | * @param keyImages
61 | * @throws BytomException
62 | */
63 | public static void restoreWallet(Client client ,Object accountImage, Object assetImage , Object keyImages) throws BytomException{
64 | Map body = new HashMap();
65 | body.put("account_image", accountImage);
66 | body.put("asset_image", assetImage);
67 | body.put("key_images", keyImages);
68 |
69 | logger.info("restore-wallet:");
70 | logger.info(body.toString());
71 |
72 | client.request("restore-wallet", body);
73 | }
74 |
75 | public static class AccountImage {
76 |
77 | public Slices[] slices;
78 |
79 | public static class Slices {
80 |
81 | @SerializedName("contract_index")
82 | public int contractIndex;
83 |
84 | public Account account;
85 |
86 | public static class Account {
87 |
88 | public String type;
89 |
90 | public List xpubs;
91 |
92 | public int quorum;
93 |
94 | @SerializedName("key_index")
95 | public int keyIndex;
96 |
97 | public String id;
98 |
99 | public String alias;
100 |
101 | }
102 |
103 | }
104 | }
105 |
106 | public static class AssetImage {
107 |
108 | public Assets[] assets;
109 |
110 | public static class Assets {
111 | public String type;
112 | public List xpubs;
113 | public int quorum;
114 | public String id;
115 | public String alias;
116 | public Map definition;
117 | @SerializedName("key_index")
118 | public int keyIndex;
119 | @SerializedName("vm_version")
120 | public int vmVersion;
121 | @SerializedName("asset_image")
122 | public String issueProgram;
123 | @SerializedName("raw_definition_byte")
124 | public String rawDefinitionByte;
125 | }
126 | }
127 |
128 | public static class KeyImages {
129 |
130 | public Xkeys[] xkeys;
131 |
132 | public static class Xkeys {
133 |
134 | public Crypto crypto;
135 | public String id;
136 | public String type;
137 | public int version;
138 | public String alias;
139 | public String xpub;
140 |
141 | public static class Crypto {
142 | public String cipher;
143 | public String ciphertext;
144 | public Map cipherparams;
145 | public String kdf;
146 | public Map kdfparams;
147 | public String mac;
148 | }
149 |
150 | }
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/common/Configuration.java:
--------------------------------------------------------------------------------
1 | package io.bytom.common;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.IOException;
5 | import java.util.Properties;
6 |
7 | public class Configuration {
8 |
9 | private static Properties props = new Properties();
10 | static {
11 | try {
12 | props.load(Thread.currentThread().getContextClassLoader()
13 | .getResourceAsStream("config.properties"));
14 | } catch (FileNotFoundException e) {
15 | e.printStackTrace();
16 | } catch (IOException e) {
17 | e.printStackTrace();
18 | }
19 | }
20 |
21 | public static String getValue(String key) {
22 | return props.getProperty(key);
23 | }
24 |
25 | public static void updateProperties(String key, String value) {
26 | props.setProperty(key, value);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/common/ParameterizedTypeImpl.java:
--------------------------------------------------------------------------------
1 | package io.bytom.common;
2 |
3 | import java.lang.reflect.ParameterizedType;
4 | import java.lang.reflect.Type;
5 |
6 | public class ParameterizedTypeImpl implements ParameterizedType {
7 |
8 | private final Class raw;
9 | private final Type[] args;
10 |
11 | public ParameterizedTypeImpl(Class raw, Type[] args) {
12 | this.raw = raw;
13 | this.args = args != null ? args : new Type[0];
14 | }
15 |
16 | @Override
17 | public Type[] getActualTypeArguments() {
18 | return args;
19 | }
20 |
21 | @Override
22 | public Type getRawType() {
23 | return raw;
24 | }
25 |
26 | @Override
27 | public Type getOwnerType() {
28 | return null;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/common/Utils.java:
--------------------------------------------------------------------------------
1 | package io.bytom.common;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 |
6 | public class Utils {
7 | public static String rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
8 | public static final Gson serializer = new GsonBuilder().setDateFormat(rfc3339DateFormat).create();
9 | }
10 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/APIException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | /**
6 | * APIException wraps errors returned by the API.
7 | * Each error contains a brief description in addition to a unique error code.
8 | * The error code can be used by Chain Support to diagnose the exact cause of the error.
9 | * The mapping of error codes to messages is as follows:
10 | *
11 | * General errors
12 | * CH001 - Request timed out
13 | * CH002 - Not found
14 | * CH003 - Invalid request body
15 | * CH004 - Invalid request header
16 | * CH006 - Not found
17 | *
18 | * Account/Asset errors
19 | * CH200 - Quorum must be greater than 1 and less than or equal to the length of xpubs
20 | * CH201 - Invalid xpub format
21 | * CH202 - At least one xpub is required
22 | * CH203 - Retrieved type does not match expected type
23 | *
24 | * Access token errors
25 | * CH300 - Malformed or empty access token id
26 | * CH301 - Access tokens must be type client or network
27 | * CH302 - Access token id is already in use
28 | * CH310 - The access token used to authenticate this request cannot be deleted
29 | *
30 | * Query errors
31 | * CH600 - Malformed pagination parameter `after`
32 | * CH601 - Incorrect number of parameters to filter
33 | * CH602 - Malformed query filter
34 | *
35 | * Transaction errors
36 | * CH700 - Reference data does not match previous transaction's reference data
37 | * CH701 - Invalid action type
38 | * CH702 - Invalid alias on action
39 | * CH730 - Missing raw transaction
40 | * CH731 - Too many signing instructions in template for transaction
41 | * CH732 - Invalid transaction input index
42 | * CH733 - Invalid signature script component
43 | * CH734 - Missing signature in template
44 | * CH735 - Transaction rejected
45 | * CH760 - Insufficient funds for tx
46 | * CH761 - Some outputs are reserved; try again
47 | */
48 | public class APIException extends BytomException {
49 | /**
50 | * Unique identifier for the error.
51 | */
52 | public String code;
53 |
54 | /**
55 | * Message describing the general nature of the error.
56 | */
57 | @SerializedName("message")
58 | public String chainMessage;
59 |
60 | /**
61 | * Additional information about the error (possibly null).
62 | */
63 | public String detail;
64 |
65 | /**
66 | * Specifies whether the error is temporary, or a change to the request is necessary.
67 | */
68 | public boolean temporary;
69 |
70 | /**
71 | * Unique identifier of the request to the server.
72 | */
73 | public String requestId;
74 |
75 | /**
76 | * HTTP status code returned by the server.
77 | */
78 | public int statusCode;
79 |
80 | /**
81 | * Initializes exception with its message and requestId attributes.
82 | * @param message error message
83 | * @param requestId unique identifier of the request
84 | */
85 | public APIException(String message, String requestId) {
86 | super(message);
87 | this.requestId = requestId;
88 | }
89 |
90 | /**
91 | * Intitializes exception with its code, message, detail & temporary field set.
92 | * @param code error code
93 | * @param message error message
94 | * @param detail additional error information
95 | * @param temporary unique identifier of the request
96 | */
97 | public APIException(String code, String message, String detail, boolean temporary) {
98 | super(message);
99 | this.chainMessage = message;
100 | this.code = code;
101 | this.detail = detail;
102 | this.temporary = temporary;
103 | }
104 |
105 | /**
106 | * Initializes exception with its code, message, detail & requestId attributes.
107 | * @param code error code
108 | * @param message error message
109 | * @param detail additional error information
110 | * @param requestId unique identifier of the request
111 | */
112 | public APIException(String code, String message, String detail, String requestId) {
113 | super(message);
114 | this.chainMessage = message;
115 | this.code = code;
116 | this.detail = detail;
117 | this.requestId = requestId;
118 | }
119 |
120 | /**
121 | * Initializes exception with all of its attributes.
122 | * @param code error code
123 | * @param message error message
124 | * @param detail additional error information
125 | * @param temporary specifies if the error is temporary
126 | * @param requestId unique identifier of the request
127 | * @param statusCode HTTP status code
128 | */
129 | public APIException(
130 | String code,
131 | String message,
132 | String detail,
133 | boolean temporary,
134 | String requestId,
135 | int statusCode) {
136 | super(message);
137 | this.chainMessage = message;
138 | this.code = code;
139 | this.detail = detail;
140 | this.temporary = temporary;
141 | this.requestId = requestId;
142 | this.statusCode = statusCode;
143 | }
144 |
145 | /**
146 | * Constructs a detailed message of the error.
147 | * @return detailed error message
148 | */
149 | @Override
150 | public String getMessage() {
151 | String s = "";
152 |
153 | if (this.code != null && this.code.length() > 0) {
154 | s += "Code: " + this.code + " ";
155 | }
156 |
157 | s += "Message: " + this.chainMessage;
158 |
159 | if (this.detail != null && this.detail.length() > 0) {
160 | s += " Detail: " + this.detail;
161 | }
162 |
163 | if (this.requestId != null) {
164 | s += " Request-ID: " + this.requestId;
165 | }
166 |
167 | return s;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/BadURLException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | /**
4 | * BadURLException wraps errors due to malformed URLs.
5 | */
6 | public class BadURLException extends BytomException {
7 | /**
8 | * Initializes exception with its message attribute.
9 | * @param message error message
10 | */
11 | public BadURLException(String message) {
12 | super(message);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/BuildException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * BuildException wraps errors returned by the build-transaction endpoint.
9 | */
10 | public class BuildException extends APIException {
11 |
12 | public BuildException(String message, String requestId) {
13 | super(message, requestId);
14 | }
15 |
16 | public static class ActionError extends APIException {
17 |
18 | public static class Data {
19 | /**
20 | * The index of the action that caused this error.
21 | */
22 | @SerializedName("index")
23 | public Integer index;
24 | }
25 |
26 | public ActionError(String message, String requestId) {
27 | super(message, requestId);
28 | }
29 |
30 | /**
31 | * Additional data pertaining to the error.
32 | */
33 | public Data data;
34 | }
35 |
36 | public static class Data {
37 | /**
38 | * A list of errors resulting from building actions.
39 | */
40 | @SerializedName("actions")
41 | public List actionErrors;
42 | }
43 |
44 | /**
45 | * Extra data associated with this error, if any.
46 | */
47 | @SerializedName("data")
48 | public Data data;
49 | }
50 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/BytomException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | /**
4 | * Base exception class for the Chain Core SDK.
5 | */
6 | public class BytomException extends Exception {
7 | /**
8 | * Default constructor.
9 | */
10 | public BytomException() {
11 | super();
12 | }
13 |
14 | /**
15 | * Initializes exception with its message attribute.
16 | *
17 | * @param message error message
18 | */
19 | public BytomException(String message) {
20 | super(message);
21 | }
22 |
23 | /**
24 | * Initializes a new exception while storing the original cause.
25 | *
26 | * @param message the error message
27 | * @param cause the cause of the exception
28 | */
29 | public BytomException(String message, Throwable cause) {
30 | super(message, cause);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/ConfigurationException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | /**
4 | * ConfigurationException wraps errors during client configuration.
5 | */
6 | public class ConfigurationException extends BytomException {
7 | /**
8 | * Initializes exception with its message attribute.
9 | * @param message error message
10 | */
11 | public ConfigurationException(String message) {
12 | super(message);
13 | }
14 |
15 | /**
16 | * Initializes new exception while storing original cause.
17 | * @param message the error message
18 | * @param cause the original cause
19 | */
20 | public ConfigurationException(String message, Throwable cause) {
21 | super(message, cause);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/ConnectivityException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | import com.squareup.okhttp.Response;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | * ConnectivityException wraps errors due to connectivity issues with the server.
9 | */
10 | public class ConnectivityException extends BytomException {
11 | /**
12 | * Initializes exception with its message attribute.
13 | * @param resp the server response used to create error message
14 | */
15 | public ConnectivityException(Response resp) {
16 | super(formatMessage(resp));
17 | }
18 |
19 | /**
20 | * Parses the the server response into a detailed error message.
21 | * @param resp the server response
22 | * @return detailed error message
23 | */
24 | private static String formatMessage(Response resp) {
25 | String s =
26 | "Response HTTP header field Chain-Request-ID is unset. There may be network issues. Please check your local network settings.";
27 | // TODO(kr): include client-generated reqid here once we have that.
28 | String body;
29 | try {
30 | body = resp.body().string();
31 | } catch (IOException ex) {
32 | body = "[unable to read response body: " + ex.toString() + "]";
33 | }
34 | return String.format("%s status=%d body=%s", s, resp.code(), body);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/HTTPException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | /**
4 | * HTTPException wraps generic HTTP errors.
5 | */
6 | public class HTTPException extends BytomException {
7 | /**
8 | * Initializes exception with its message attribute.
9 | * @param message error message
10 | */
11 | public HTTPException(String message) {
12 | super(message);
13 | }
14 |
15 | /**
16 | * Initializes new exception while storing original cause.
17 | * @param message the error message
18 | * @param cause the original cause
19 | */
20 | public HTTPException(String message, Throwable cause) {
21 | super(message, cause);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/exception/JSONException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.exception;
2 |
3 | /**
4 | * JSONException wraps errors due to marshaling/unmarshaling json payloads.
5 | */
6 | public class JSONException extends BytomException {
7 |
8 | /**
9 | * Unique indentifier of the request to the server.
10 | */
11 | public String requestId;
12 |
13 | /**
14 | * Default constructor.
15 | */
16 | public JSONException(String message) {
17 | super(message);
18 | }
19 |
20 | /**
21 | * Initializes exception with its message and requestId attributes.
22 | * Use this constructor in context of an API call.
23 | *
24 | * @param message error message
25 | * @param requestId unique identifier of the request
26 | */
27 | public JSONException(String message, String requestId) {
28 | super(message);
29 | this.requestId = requestId;
30 | }
31 |
32 | public String getMessage() {
33 | String message = "Message: " + super.getMessage();
34 | if (requestId != null) {
35 | message += " Request-ID: " + requestId;
36 | }
37 | return message;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/http/BatchResponse.java:
--------------------------------------------------------------------------------
1 | package io.bytom.http;
2 |
3 | import io.bytom.exception.*;
4 | import com.google.gson.Gson;
5 | import com.google.gson.JsonArray;
6 | import com.google.gson.JsonElement;
7 | import com.google.gson.JsonParser;
8 | import com.squareup.okhttp.Response;
9 |
10 | import java.io.IOException;
11 | import java.lang.reflect.Type;
12 | import java.util.*;
13 |
14 | /**
15 | * BatchResponse provides a convenient interface for handling the results of
16 | * batched API calls. The response contains one success or error per outgoing
17 | * request item in the batch. Errors are always of type APIExcpetion.
18 | */
19 | public class BatchResponse {
20 | private Response response;
21 | private Map successesByIndex = new LinkedHashMap<>();
22 | private Map errorsByIndex = new LinkedHashMap<>();
23 |
24 | /**
25 | * This constructor is used when deserializing a response from an API call.
26 | */
27 | public BatchResponse(Response response, Gson serializer, Type tClass, Type eClass)
28 | throws BytomException, IOException {
29 | this.response = response;
30 |
31 | try {
32 | JsonArray root = new JsonParser().parse(response.body().charStream()).getAsJsonArray();
33 | for (int i = 0; i < root.size(); i++) {
34 | JsonElement elem = root.get(i);
35 |
36 | // Test for interleaved errors
37 | APIException err = serializer.fromJson(elem, eClass);
38 | if (err.code != null) {
39 | errorsByIndex.put(i, err);
40 | continue;
41 | }
42 |
43 | successesByIndex.put(i, (T) serializer.fromJson(elem, tClass));
44 | }
45 | } catch (IllegalStateException e) {
46 | throw new JSONException(
47 | "Unable to read body: " + e.getMessage(), response.headers().get("Chain-Request-ID"));
48 | }
49 | }
50 |
51 | /**
52 | * This constructor is used for synthetically generating a batch response
53 | * object from a map of successes and a map of errors. It ensures that
54 | * the successes and errors are stored in an order-preserving fashion.
55 | */
56 | public BatchResponse(Map successes, Map errors) {
57 | List successIndexes = new ArrayList<>();
58 | Iterator successIter = successes.keySet().iterator();
59 | while (successIter.hasNext()) successIndexes.add(successIter.next());
60 | Collections.sort(successIndexes);
61 | for (int i : successIndexes) successesByIndex.put(i, successes.get(i));
62 |
63 | List errorIndexes = new ArrayList<>();
64 | Iterator errorIter = errors.keySet().iterator();
65 | while (errorIter.hasNext()) errorIndexes.add(errorIter.next());
66 | Collections.sort(errorIndexes);
67 | for (int i : errorIndexes) errorsByIndex.put(i, errors.get(i));
68 | }
69 |
70 | /**
71 | * Returns the internal response object.
72 | */
73 | public Response response() {
74 | return response;
75 | }
76 |
77 | /**
78 | * Returns the total number of response objects. This should equal the number
79 | * of request objects in the batch.
80 | */
81 | public int size() {
82 | return successesByIndex.size() + errorsByIndex.size();
83 | }
84 |
85 | /**
86 | * Returns whether the request object at the given index produced a success.
87 | * @param index the index of the request object
88 | */
89 | public boolean isSuccess(int index) {
90 | return successesByIndex.containsKey(index);
91 | }
92 |
93 | /**
94 | * Returns whether the request object at the given index produced an error.
95 | * @param index the index of the request object
96 | */
97 | public boolean isError(int index) {
98 | return errorsByIndex.containsKey(index);
99 | }
100 |
101 | /**
102 | * Returns a list of successful response objects in the batch. The order of
103 | * the list corresponds to the order of the request objects that produced the
104 | * successes.
105 | */
106 | public List successes() {
107 | List res = new ArrayList<>();
108 | res.addAll(successesByIndex.values());
109 | return res;
110 | }
111 |
112 | /**
113 | * Returns a list of error objects in the batch. The order of the list
114 | * corresponds to the order of the request objects that produced the
115 | * errors.
116 | */
117 | public List errors() {
118 | List res = new ArrayList<>();
119 | res.addAll(errorsByIndex.values());
120 | return res;
121 | }
122 |
123 | /**
124 | * Returns a map of success responses, keyed by the index of the request
125 | * object that produced the success. The set of this map's keys is mutually
126 | * exclusive of the keys returned by errorsByIndex.
127 | */
128 | public Map successesByIndex() {
129 | return successesByIndex;
130 | }
131 |
132 | /**
133 | * Returns a map of error responses, keyed by the index of the request
134 | * object that produced the error. The set of this map's keys is mutually
135 | * exclusive of the keys returned by successByIndex.
136 | */
137 | public Map errorsByIndex() {
138 | return errorsByIndex;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/java-sdk/src/main/java/io/bytom/http/SuccessMessage.java:
--------------------------------------------------------------------------------
1 | package io.bytom.http;
2 |
3 | /**
4 | * This class represents RPC success responses whose content is not meaningful.
5 | */
6 | public class SuccessMessage {
7 | public String message;
8 | }
9 |
--------------------------------------------------------------------------------
/java-sdk/src/main/resources/config.properties:
--------------------------------------------------------------------------------
1 | bytom.api.url=
2 | client.access.token=
3 |
4 | # bytom.api.url=http://10.100.7.47:9888/
5 | # client.access.token=wt:3d17dbb953cedd53353bf3f342bb2929e9505105ffeb21670e6bd00abeef3772
6 |
7 | #bytom.api.url=http://127.0.0.1:9888/
8 | #client.access.token=sheng:49d1623f5991c62a5094e761477ddd2838dceb49c22fbf84b492a54f1df88123
9 |
--------------------------------------------------------------------------------
/java-sdk/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=debug, stdout, R
2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
4 |
5 | log4j.logger.org.apache.commons.httpclient=info
6 | log4j.logger.httpclient.wire.content=info
7 | log4j.logger.httpclient.wire.header=info
8 |
9 | # Pattern to output the caller's file name and line number.
10 | log4j.appender.stdout.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n
11 |
12 | log4j.appender.R=org.apache.log4j.RollingFileAppender
13 | log4j.appender.R.File=bytom.log
14 | log4j.appender.R.MaxFileSize= 1000KB
15 |
16 | # Keep one backup file
17 | log4j.appender.R.MaxBackupIndex=1
18 |
19 | log4j.appender.R.layout=org.apache.log4j.PatternLayout
20 | log4j.appender.R.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/AppTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom;
2 |
3 | import com.google.gson.Gson;
4 | import com.squareup.okhttp.*;
5 | import io.bytom.api.Block;
6 | import org.junit.Test;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * Unit test for simple App.
12 | */
13 | public class AppTest {
14 | private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
15 |
16 | private final OkHttpClient client = new OkHttpClient();
17 |
18 |
19 | @Test
20 | public void testListAccounts() throws IOException {
21 |
22 | String postBody = "{}";
23 |
24 | Request request = new Request.Builder()
25 | .url("http://127.0.0.1:9888/list-accounts")
26 | .post(RequestBody.create(JSON, postBody))
27 | .build();
28 |
29 | Response response = client.newCall(request).execute();
30 |
31 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
32 |
33 | System.out.println(response.body().string());
34 | }
35 |
36 |
37 | @Test
38 | public void testCreateAccount() throws IOException {
39 |
40 | String postBody = "{\n" +
41 | " \"root_xpubs\": [\n" +
42 | " \"012454d25928d52d42e3ee1f2bebe0916974d958f9ec08c9a028043ffe3dd95630c1b788c947b8c07ede2a4b5e3e3bbe0e305bab4526a7bc67b21e1d051e74ef\"\n" +
43 | " ], \n" +
44 | " \"quorum\": 1, \n" +
45 | " \"alias\": \"sheng\"\n" +
46 | "}";
47 |
48 | Request request = new Request.Builder()
49 | .url("http://127.0.0.1:9888/create-account")
50 | .post(RequestBody.create(JSON, postBody))
51 | .build();
52 |
53 | Response response = client.newCall(request).execute();
54 |
55 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
56 |
57 | System.out.println(response.body().string());
58 | }
59 |
60 | @Test
61 | public void testBlockGet() throws Exception {
62 | String postBody = "{\n" +
63 | " \"hash\": \"37eabf4d321f43b930f52b3af2e53b2ad2dbb234949e778213a4a54e4be04ea8\",\n" +
64 | " \"size\": 388,\n" +
65 | " \"version\": 1,\n" +
66 | " \"height\": 158,\n" +
67 | " \"previous_block_hash\": \"2cfa73d6ed14a7fcb3a8ccc2d4c45b0fd4c7754ec6fafaa0964045c23e2131cd\",\n" +
68 | " \"timestamp\": 1527241075,\n" +
69 | " \"nonce\": 0,\n" +
70 | " \"bits\": 2305843009214532812,\n" +
71 | " \"difficulty\": \"5789598940468732338727519302629705571043137272394850465663635070376277442560\",\n" +
72 | " \"transaction_merkle_root\": \"62755c4770374e696c4fecd94e022ccf6f2adc00f409ac694a5cb92fe02353eb\",\n" +
73 | " \"transaction_status_hash\": \"c9c377e5192668bc0a367e4a4764f11e7c725ecced1d7b6a492974fab1b6d5bc\",\n" +
74 | " \"transactions\": [\n" +
75 | " {\n" +
76 | " \"id\": \"5c38b8107d53cbfebd19adbd11f2839d914099a74e491f1cf75f8b18320c84e2\",\n" +
77 | " \"version\": 1,\n" +
78 | " \"size\": 77,\n" +
79 | " \"time_range\": 0,\n" +
80 | " \"inputs\": [\n" +
81 | " {\n" +
82 | " \"type\": \"coinbase\",\n" +
83 | " \"asset_id\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n" +
84 | " \"asset_definition\": {},\n" +
85 | " \"amount\": 0,\n" +
86 | " \"arbitrary\": \"c29e\"\n" +
87 | " }\n" +
88 | " ],\n" +
89 | " \"outputs\": [\n" +
90 | " {\n" +
91 | " \"type\": \"control\",\n" +
92 | " \"id\": \"9acda4f2bd83c7114a8837b596860df773a7e61b1d6b0a774addda1473361ad4\",\n" +
93 | " \"position\": 0,\n" +
94 | " \"asset_id\": \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\n" +
95 | " \"asset_definition\": {},\n" +
96 | " \"amount\": 41250000000,\n" +
97 | " \"control_program\": \"001412a47b53b3bbdaf9f3510e1d8c19d69f81ca9142\",\n" +
98 | " \"address\": \"sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h\"\n" +
99 | " }\n" +
100 | " ],\n" +
101 | " \"status_fail\": false\n" +
102 | " }\n" +
103 | " ]\n" +
104 | " }";
105 | Gson gson = new Gson();
106 | Block block = gson.fromJson(postBody, Block.class);
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/TestUtils.java:
--------------------------------------------------------------------------------
1 | package io.bytom;
2 |
3 | import io.bytom.common.Configuration;
4 | import io.bytom.exception.BytomException;
5 | import io.bytom.http.Client;
6 | import org.apache.log4j.Logger;
7 |
8 | /**
9 | * TestUtils provides a simplified api for testing.
10 | */
11 | public class TestUtils {
12 |
13 | public static Logger logger = Logger.getLogger(TestUtils.class);
14 |
15 | public static Client generateClient() throws BytomException {
16 |
17 | String coreURL = Configuration.getValue("bytom.api.url");
18 | String accessToken = Configuration.getValue("client.access.token");
19 |
20 | if (coreURL == null || coreURL.isEmpty()) {
21 | coreURL = "http://127.0.0.1:9888";
22 | }
23 |
24 | if (coreURL.endsWith("/")) {
25 | //split the last char "/"
26 | coreURL = coreURL.substring(0, coreURL.length()-1);
27 | }
28 |
29 | return new Client(coreURL, accessToken);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/AccessTokenTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.AccessToken;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.junit.Test;
8 |
9 | import java.util.List;
10 |
11 | public class AccessTokenTest {
12 |
13 | static Client client;
14 |
15 | static {
16 | try {
17 | client = TestUtils.generateClient();
18 | } catch (BytomException e) {
19 | e.printStackTrace();
20 | }
21 | }
22 |
23 | @Test
24 | public void testTokenCreate() throws Exception {
25 | AccessToken accessToken = new AccessToken.Builder().setId("sheng").create(client);
26 | }
27 |
28 |
29 | @Test
30 | public void testTokenList() throws Exception {
31 | List tokenList = AccessToken.list(client);
32 | }
33 |
34 | @Test
35 | public void testTokenCheck() throws Exception {
36 | String secret = "5e37378eb59de6b10e60f2247ebf71c4955002e75e0cd31ede3bf48813a0a799";
37 | AccessToken.check(client, "sheng", secret);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/AccountTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Account;
5 | import io.bytom.api.Receiver;
6 | import io.bytom.http.Client;
7 | import org.junit.Test;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import static org.junit.Assert.assertEquals;
13 | import static org.junit.Assert.assertNotNull;
14 |
15 | public class AccountTest {
16 |
17 | static Client client;
18 | static Account account;
19 |
20 | @Test
21 | public void testAccountCreate() throws Exception {
22 | client = TestUtils.generateClient();
23 |
24 | String alias = "AccountTest.testAccountCreate.002";
25 | Integer quorum = 1;
26 | List root_xpubs = new ArrayList<>();
27 | root_xpubs.add("c4b25825e92cd8623de4fd6a35952ad0efb2ed215fdb1b40754f0ed12eff7827d147d1e8b003601ba2f78a4a84dcc77e93ed282633f2679048c5d5ac5ea10cb5");
28 |
29 |
30 | // Key.Builder builder = new Key.Builder().setAlias(alias).setPassword(password);
31 | // key = Key.create(client, builder);
32 |
33 | Account.Builder builder = new Account.Builder().setAlias(alias).setQuorum(quorum).setRootXpub(root_xpubs);
34 | account = Account.create(client, builder);
35 |
36 | assertNotNull(account.id);
37 | assertEquals(alias.toLowerCase(), account.alias);
38 | }
39 |
40 | @Test
41 | public void testAccountList() throws Exception {
42 | client = TestUtils.generateClient();
43 | List accountList = Account.list(client);
44 | }
45 |
46 | @Test
47 | public void testAccountDelete() throws Exception {
48 | client = TestUtils.generateClient();
49 | List accountList = Account.list(client);
50 | String alias = accountList.get(accountList.size()-1).alias;
51 | //delete the last Account Object
52 | Account.delete(client, alias);
53 | }
54 |
55 | @Test
56 | public void testReceiverCreate() throws Exception {
57 | client = TestUtils.generateClient();
58 | List accountList = Account.list(client);
59 | String alias = accountList.get(accountList.size()-1).alias;
60 | String id = accountList.get(accountList.size()-1).id;
61 |
62 | Account.ReceiverBuilder receiverBuilder = new Account.ReceiverBuilder().setAccountAlias(alias).setAccountId(id);
63 | Receiver receiver = receiverBuilder.create(client);
64 |
65 | assertNotNull(receiver.address);
66 | assertNotNull(receiver.controlProgram);
67 | }
68 |
69 | @Test
70 | public void testAddressList() throws Exception {
71 | client = TestUtils.generateClient();
72 | List accountList = Account.list(client);
73 | String alias = accountList.get(accountList.size()-1).alias;
74 | String id = accountList.get(accountList.size()-1).id;
75 |
76 | Account.AddressBuilder addressBuilder = new Account.AddressBuilder().setAccountId(id).setAccountAlias(alias);
77 | List addressList = addressBuilder.list(client);
78 |
79 | assertNotNull(addressList);
80 | }
81 |
82 | @Test
83 | public void testAddressValidate() throws Exception {
84 | client = TestUtils.generateClient();
85 |
86 | List accountList = Account.list(client);
87 | String alias = accountList.get(accountList.size()-1).alias;
88 | String id = accountList.get(accountList.size()-1).id;
89 |
90 | Account.AddressBuilder addressBuilder = new Account.AddressBuilder().setAccountId(id).setAccountAlias(alias);
91 | List addressList = addressBuilder.list(client);
92 |
93 | Account.Address address = addressBuilder.validate(client, addressList.get(0).address);
94 | assertEquals(true, address.is_local);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/AssetTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Account;
5 | import io.bytom.api.Asset;
6 | import io.bytom.http.Client;
7 | import org.junit.Test;
8 |
9 | import java.util.List;
10 |
11 | import static org.junit.Assert.assertEquals;
12 | import static org.junit.Assert.assertNotNull;
13 |
14 | public class AssetTest {
15 |
16 | static Client client;
17 | static Account account;
18 | static Asset asset;
19 |
20 | @Test
21 | public void testAssetCreate() throws Exception {
22 | client = TestUtils.generateClient();
23 |
24 | List accountList = Account.list(client);
25 | String alias = "GOLD";
26 |
27 | List xpubs = accountList.get(0).xpubs;
28 |
29 | Asset.Builder builder = new Asset.Builder()
30 | .setAlias(alias)
31 | .setQuorum(1)
32 | .setRootXpubs(xpubs);
33 | asset = builder.create(client);
34 | assertNotNull(asset);
35 | }
36 |
37 | @Test
38 | public void testAssetGet() throws Exception {
39 | client = TestUtils.generateClient();
40 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder();
41 | String id = queryBuilder.list(client).get(1).id;
42 | queryBuilder.setId(id);
43 | Asset asset = queryBuilder.get(client);
44 | }
45 |
46 | @Test
47 | public void testAssetList() throws Exception {
48 | client = TestUtils.generateClient();
49 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder();
50 | List assetList = queryBuilder.list(client);
51 | assertEquals(2, assetList.size());
52 | }
53 |
54 | @Test
55 | public void testUpdateAssetAlias() throws Exception {
56 | client = TestUtils.generateClient();
57 |
58 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder();
59 | String id = queryBuilder.list(client).get(1).id;
60 |
61 | String alias = "HELLOWORLD";
62 |
63 |
64 | Asset.AliasUpdateBuilder aliasUpdateBuilder =
65 | new Asset.AliasUpdateBuilder()
66 | .setAlias(alias)
67 | .setAssetId(id);
68 | aliasUpdateBuilder.update(client);
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/BalanceTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Balance;
5 | import io.bytom.api.Wallet;
6 | import io.bytom.exception.BytomException;
7 | import io.bytom.http.Client;
8 | import org.apache.log4j.Logger;
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 |
12 | import java.util.List;
13 |
14 | public class BalanceTest {
15 |
16 | static Client client;
17 |
18 | static {
19 | try {
20 | client = TestUtils.generateClient();
21 | } catch (BytomException e) {
22 | e.printStackTrace();
23 | }
24 | }
25 |
26 | static Balance balance;
27 |
28 | @Test
29 | public void testBalanceList() throws Exception {
30 | List balanceList = new Balance.QueryBuilder().list(client);
31 | Assert.assertNotNull(balanceList);
32 | }
33 |
34 | @Test
35 | public void testBalanceByAssetAlias() throws Exception {
36 | Balance balance = new Balance.QueryBuilder().listByAssetAlias(client, "BTM");
37 | Assert.assertNotNull(balance);
38 | }
39 |
40 | @Test
41 | public void testBalanceByAccountAlias() throws Exception {
42 | List balanceList = new Balance.QueryBuilder().listByAccountAlias(client, "test");
43 | Assert.assertNotNull(balanceList);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/BlockTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Asset;
5 | import io.bytom.api.Block;
6 | import io.bytom.api.Wallet;
7 | import io.bytom.exception.BytomException;
8 | import io.bytom.http.Client;
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 |
12 | public class BlockTest {
13 |
14 | static Client client;
15 |
16 | static {
17 | try {
18 | client = TestUtils.generateClient();
19 | } catch (BytomException e) {
20 | e.printStackTrace();
21 | }
22 | }
23 |
24 | static Block block;
25 | static Block.BlockHeader blockHeader;
26 | static Block.BlockDifficulty blockDifficulty;
27 | static Block.BlockHashRate blockHashRate;
28 |
29 | @Test
30 | public void testBlockCountGet() throws Exception {
31 | int count = Block.getBlockCount(client);
32 | Assert.assertEquals(158, count);
33 | }
34 |
35 | @Test
36 | public void testBlockHashGet() throws Exception {
37 | String blockHash = Block.getBlockHash(client);
38 | Assert.assertNotNull(blockHash);
39 | }
40 |
41 | @Test
42 | public void testBlockGet() throws Exception {
43 | int height = Block.getBlockCount(client);
44 | String blockHash = Block.getBlockHash(client);
45 |
46 | block = new Block.QueryBuilder()
47 | .setBlockHeight(height)
48 | .setBlockHash(blockHash)
49 | .getBlock(client);
50 | }
51 |
52 | @Test
53 | public void testBlockHeader() throws Exception {
54 | int height = Block.getBlockCount(client);
55 | String blockHash = Block.getBlockHash(client);
56 |
57 | blockHeader = new Block.QueryBuilder()
58 | .setBlockHeight(height)
59 | .setBlockHash(blockHash)
60 | .getBlockHeader(client);
61 | }
62 |
63 | @Test
64 | public void testBlockDifficulty() throws Exception {
65 | int height = Block.getBlockCount(client);
66 | String blockHash = Block.getBlockHash(client);
67 |
68 | blockDifficulty = new Block.QueryBuilder()
69 | .setBlockHeight(height)
70 | .setBlockHash(blockHash)
71 | .getBlockDifficulty(client);
72 | }
73 |
74 | @Test
75 | public void testBlockHashRate() throws Exception {
76 | int height = Block.getBlockCount(client);
77 | String blockHash = Block.getBlockHash(client);
78 |
79 | blockHashRate = new Block.QueryBuilder()
80 | .setBlockHeight(height)
81 | .setBlockHash(blockHash)
82 | .getHashRate(client);
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/CoreConfigTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.CoreConfig;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | public class CoreConfigTest {
11 |
12 | static Client client;
13 |
14 | static {
15 | try {
16 | client = TestUtils.generateClient();
17 | } catch (BytomException e) {
18 | e.printStackTrace();
19 | }
20 | }
21 |
22 | static CoreConfig.NetInfo netInfo;
23 |
24 | static Integer gasRate;
25 |
26 | @Test
27 | public void testNetInfo() throws Exception {
28 | netInfo = CoreConfig.getNetInfo(client);
29 | Assert.assertNotNull(netInfo);
30 | }
31 |
32 | @Test
33 | public void testGasRate() throws Exception {
34 | gasRate = CoreConfig.getGasRate(client);
35 | Assert.assertNotNull(gasRate);
36 | }
37 |
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/KeyTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Key;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.junit.Test;
8 |
9 | import java.util.List;
10 |
11 | import static org.junit.Assert.assertEquals;
12 | import static org.junit.Assert.assertNotEquals;
13 | import static org.junit.Assert.assertNotNull;
14 |
15 | public class KeyTest {
16 |
17 | static Client client;
18 | static Key key;
19 |
20 | @Test
21 | public void testClientKeyCreate() throws Exception {
22 | client = TestUtils.generateClient();
23 |
24 | String alias = "KeyTest.testKeyCreate.successli004";
25 | String password = "123456";
26 |
27 | Key.Builder builder = new Key.Builder().setAlias(alias).setPassword(password);
28 | key = Key.create(client, builder);
29 |
30 | assertNotNull(key.xpub);
31 | assertEquals(alias.toLowerCase(), key.alias);
32 | }
33 |
34 | @Test
35 | public void testClientKeyList() throws Exception {
36 | //client = TestUtils.generateClient();
37 | client = new Client("http://127.0.0.1:9888/");
38 | List keyList = Key.list(client);
39 | }
40 |
41 | @Test
42 | public void testClientKeyDelete() throws Exception {
43 | client = TestUtils.generateClient();
44 | List keyList = Key.list(client);
45 | String xpub = keyList.get(keyList.size()-1).xpub;
46 | //delete the last Key Object
47 | Key.delete(client, xpub, "123456");
48 | }
49 |
50 | @Test
51 | public void testClientKeyResetPassword() throws BytomException {
52 | client = TestUtils.generateClient();
53 | List keyList = Key.list(client);
54 | String xpub = keyList.get(keyList.size()-1).xpub;
55 | Key.resetPassword(client, xpub, "123456", "123456789");
56 | Key.delete(client, xpub, "123456789");
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/MessageTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Message;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | public class MessageTest {
11 |
12 | static Client client;
13 |
14 | static {
15 | try {
16 | client = TestUtils.generateClient();
17 | } catch (BytomException e) {
18 | e.printStackTrace();
19 | }
20 | }
21 |
22 | static Message message;
23 |
24 | @Test
25 | public void testMessageSign() throws Exception {
26 |
27 | message = new Message.SignBuilder()
28 | .setAddress("sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h")
29 | .setMessage("test")
30 | .setPassword("123456")
31 | .sign(client);
32 |
33 | Assert.assertNotNull(message);
34 | }
35 |
36 | @Test
37 | public void testMessageVerify() throws Exception {
38 | String derived_xpub = "6e1efce70e2b29efa348aec7c148edc2beb72edc0d4422a03cfb0f40e6e4cfc6e6050b5863bbe84c44131280dff68614e0308a4d081e8b677d0f7f09fb3390c4";
39 | String signature = "0d840d5b6a6df028013260e94e871c1443686c446a65db4ee93005c5395c3607feb0ac5bf583a3139c8a3d0afe757448ff49fa17ffd2377831ce5f925c846b0b";
40 |
41 | Boolean verify = new Message.VerifyBuilder()
42 | .setDerivedXpub(derived_xpub)
43 | .setSignature(signature)
44 | .setAddress("sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h")
45 | .setMessage("test")
46 | .verifyMessage(client);
47 | }
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/RawTransactionTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.RawTransaction;
5 | import io.bytom.api.Wallet;
6 | import io.bytom.exception.BytomException;
7 | import io.bytom.http.Client;
8 | import org.junit.Assert;
9 | import org.junit.Test;
10 |
11 | public class RawTransactionTest {
12 |
13 | static Client client;
14 |
15 | static {
16 | try {
17 | client = TestUtils.generateClient();
18 | } catch (BytomException e) {
19 | e.printStackTrace();
20 | }
21 | }
22 |
23 | static RawTransaction rawTransaction;
24 |
25 |
26 | @Test
27 | public void testRawTxDecode() throws Exception {
28 | String rawTxId = "070100010161015f30e052cd50e385951936c08fb5642bd12b727da958960249ddad8c9a77e5371fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d59901000116001412a47b53b3bbdaf9f3510e1d8c19d69f81ca91426302405a8b5adebd2ab63ba1ac55a7bbadc1fe246806c37732df0442b827fa4e06058137711d9d012becdc9de507a8ad0de0dd50780c0503c0dcff2dc03d7592e31a08206e1efce70e2b29efa348aec7c148edc2beb72edc0d4422a03cfb0f40e6e4cfc602013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d3bdc6980101160014a9c0ea4abe4d09546197bac3c86f4dd39fde1afb00013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084af5f011600144cc6edb1f4077c740e0201bb3688e6efba4a098c00";
29 | rawTransaction = RawTransaction.decode(client, rawTxId);
30 | Assert.assertNotNull(rawTransaction);
31 | }
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/UTXOTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.UnspentOutput;
5 | import io.bytom.exception.BytomException;
6 | import io.bytom.http.Client;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | import java.util.List;
11 |
12 | public class UTXOTest {
13 |
14 | static Client client;
15 |
16 | static {
17 | try {
18 | client = TestUtils.generateClient();
19 | } catch (BytomException e) {
20 | e.printStackTrace();
21 | }
22 | }
23 |
24 |
25 | @Test
26 | public void testBalanceList() throws Exception {
27 | List UTXOList = new UnspentOutput.QueryBuilder().list(client);
28 | Assert.assertNotNull(UTXOList);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/UnconfirmedTransactionTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.UnconfirmedTransaction;
5 | import io.bytom.api.Wallet;
6 | import io.bytom.exception.BytomException;
7 | import io.bytom.http.Client;
8 | import org.junit.Assert;
9 | import org.junit.Test;
10 |
11 | public class UnconfirmedTransactionTest {
12 |
13 | static Client client;
14 |
15 | static {
16 | try {
17 | client = TestUtils.generateClient();
18 | } catch (BytomException e) {
19 | e.printStackTrace();
20 | }
21 | }
22 |
23 | static UnconfirmedTransaction unconfirmedTransaction;
24 | static UnconfirmedTransaction.UTXResponse utxResponse;
25 |
26 | @Test
27 | public void testUCTXList() throws Exception {
28 | utxResponse = UnconfirmedTransaction.list(client);
29 | Assert.assertNotNull(utxResponse);
30 | }
31 |
32 | @Test
33 | public void testUCTXGet() throws Exception {
34 | utxResponse = UnconfirmedTransaction.list(client);
35 | unconfirmedTransaction = UnconfirmedTransaction.get(client, utxResponse.txIds.get(0));
36 | Assert.assertNotNull(unconfirmedTransaction);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/java-sdk/src/test/java/io/bytom/integration/WalletTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.integration;
2 |
3 | import io.bytom.TestUtils;
4 | import io.bytom.api.Key;
5 | import io.bytom.api.Wallet;
6 | import io.bytom.exception.BytomException;
7 | import io.bytom.http.Client;
8 | import org.junit.Test;
9 |
10 | import static org.junit.Assert.assertEquals;
11 | import static org.junit.Assert.assertNotNull;
12 |
13 | public class WalletTest {
14 |
15 | static Client client;
16 |
17 | static {
18 | try {
19 | client = TestUtils.generateClient();
20 | } catch (BytomException e) {
21 | e.printStackTrace();
22 | }
23 | }
24 |
25 | static Wallet wallet;
26 |
27 | @Test
28 | public void testWalletBackUp() throws Exception {
29 |
30 | wallet = Wallet.backupWallet(client);
31 | }
32 |
33 | @Test
34 | public void testWallerRestore() throws Exception {
35 | // get wallet object
36 | wallet = Wallet.backupWallet(client);
37 | //restore
38 | Wallet.restoreWallet(client, wallet.accountImage, wallet.assetImage, wallet.keyImages);
39 | }
40 |
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 | 4.0.0
6 |
7 | io.bytom
8 | bytom-sdk
9 | 1.0.2
10 |
11 |
12 |
13 | org.apache.maven.plugins
14 | maven-compiler-plugin
15 |
16 | 7
17 | 7
18 |
19 |
20 |
21 |
22 |
23 | java-sdk
24 | tx-signer
25 |
26 | pom
27 |
28 | bytom-sdk-java
29 | https://github.com/Bytom
30 | SDK for Bytom project in github.
31 |
32 |
33 |
34 | Apache License, Version 2.0
35 | http://www.apache.org/licenses/LICENSE-2.0.txt
36 | repo
37 |
38 |
39 |
40 |
41 |
42 | https://github.com/Bytom/bytom-java-sdk/issues
43 | GitHub Issues
44 |
45 |
46 |
47 |
48 | successli
49 | successli@outlook.com
50 | https://github.com/successli
51 |
52 |
53 |
54 |
55 | scm:git:git@github.com:Bytom/bytom-java-sdk.git
56 | scm:git:git@github.com:Bytom/bytom-java-sdk.git
57 | git@github.com:Bytom/bytom-java-sdk.git
58 |
59 |
60 |
61 | UTF-8
62 |
63 |
64 |
65 |
66 | junit
67 | junit
68 | 4.12
69 | test
70 |
71 |
72 | log4j
73 | log4j
74 | 1.2.17
75 |
76 |
77 | com.squareup.okhttp
78 | okhttp
79 | 2.5.0
80 |
81 |
82 | com.squareup.okhttp
83 | mockwebserver
84 | 2.5.0
85 | test
86 |
87 |
88 | com.google.code.gson
89 | gson
90 | 2.8.0
91 |
92 |
93 | org.bouncycastle
94 | bcpkix-jdk15on
95 | 1.56
96 |
97 |
98 |
99 |
100 |
101 | release
102 |
103 |
104 |
105 | org.apache.maven.plugins
106 | maven-source-plugin
107 | 2.2.1
108 |
109 |
110 | attach-sources
111 |
112 | jar-no-fork
113 |
114 |
115 |
116 |
117 |
118 | org.apache.maven.plugins
119 | maven-javadoc-plugin
120 | 2.9.1
121 |
122 |
123 | attach-javadocs
124 |
125 | jar
126 |
127 |
128 |
129 |
130 |
131 | org.apache.maven.plugins
132 | maven-gpg-plugin
133 | 1.5
134 |
135 |
136 | sign-artifacts
137 | verify
138 |
139 | sign
140 |
141 |
142 |
143 |
144 |
145 | maven-assembly-plugin
146 | 2.3
147 |
148 |
149 | jar-with-dependencies
150 |
151 |
152 |
153 |
154 | package
155 |
156 | single
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | oss
169 | https://oss.sonatype.org/content/repositories/snapshots
170 |
171 |
172 | oss
173 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/tx-signer/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # tx-signer
4 |
5 | Java implementation of signing transaction offline.
6 |
7 | ## Pre
8 |
9 | #### Add dependency to your project
10 |
11 | 1. first get source code
12 |
13 | ```
14 | git clone https://github.com/Bytom/bytom-java-sdk.git
15 | ```
16 |
17 | 2. install to maven repository
18 |
19 | ```
20 | $ mvn clean install -DskipTests
21 | ```
22 |
23 | 3. add dependency
24 | ```xml
25 |
26 | io.bytom
27 | tx-signer
28 | 1.0.2
29 |
30 | ```
31 |
32 | ## Example
33 |
34 | #### build transaction with spend input
35 | ```
36 | String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
37 |
38 | // create spend input
39 | SpendInput input = new SpendInput();
40 | input.setAssetId(btmAssetID);
41 | input.setAmount(9800000000L);
42 |
43 | // control program of spend utxo
44 | input.setProgram("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");
45 |
46 | // source position of spend utxo
47 | input.setSourcePosition(2);
48 |
49 | // source id of spend utxo
50 | input.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");
51 |
52 | // is the spend utxo used for change
53 | input.setChange(true);
54 |
55 | // BIP protocol for derived paths, default BIP44
56 | input.setBipProtocol(BIPProtocol.BIP32);
57 |
58 | // contorl program index of spend utxo
59 | input.setControlProgramIndex(457);
60 |
61 | // account index
62 | input.setKeyIndex(1);
63 |
64 | // provide a root private key for signing
65 | input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
66 |
67 | // build transaction with signature
68 | Transaction tx = new Transaction.Builder()
69 | .addInput(input)
70 | .addOutput(new Output(btmAssetID, 8800000000L, "0014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c"))
71 | .addOutput(new Output(btmAssetID, 900000000L, "00200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac66"))
72 | .setTimeRange(0)
73 | .build();
74 |
75 | String rawTransaction = tx.rawTransaction();
76 | ```
77 |
78 | #### build transaction with issuance input
79 | ```
80 | IssuanceInput issuanceInput = new IssuanceInput();
81 | issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");
82 | issuanceInput.setAmount(100000000000L);
83 |
84 | // issuance program
85 | issuanceInput.setProgram("ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad");
86 | issuanceInput.setNonce("ac9d5a527f5ab00a");
87 |
88 | // asset index
89 | issuanceInput.setKeyIndex(5);
90 |
91 | // raw asset definition
92 | issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d");
93 |
94 | // provide a root private key for signing
95 | issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
96 |
97 | SpendInput spendInput = new SpendInput();
98 | spendInput.setAssetId(btmAssetID);
99 | spendInput.setAmount(9800000000L);
100 | spendInput.setProgram("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");
101 | spendInput.setKeyIndex(1);
102 | spendInput.setChange(true);
103 | spendInput.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");
104 | spendInput.setSourcePosition(2);
105 | spendInput.setControlProgramIndex(457);
106 | spendInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
107 |
108 | Transaction tx = new Transaction.Builder()
109 | .addInput(issuanceInput)
110 | .addInput(spendInput)
111 | .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449"))
112 | .addOutput(new Output(btmAssetID, 9700000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56"))
113 | .setTimeRange(0)
114 | .build();
115 |
116 | String rawTx = tx.rawTransaction();
117 |
118 | ```
--------------------------------------------------------------------------------
/tx-signer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bytom-sdk
7 | io.bytom
8 | 1.0.2
9 |
10 | 4.0.0
11 |
12 | tx-signer
13 | 1.0.2
14 |
15 |
16 |
17 |
18 | com.google.crypto.tink
19 | tink
20 | 1.1.1
21 |
22 |
23 | com.google.protobuf
24 | protobuf-java
25 |
26 |
27 |
28 |
29 |
30 | org.bouncycastle
31 | bcprov-ext-jdk15on
32 | 1.55
33 |
34 |
35 |
36 | org.bouncycastle
37 | bcprov-jdk15on
38 | 1.59
39 |
40 |
41 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/com/google/crypto/tink/subtle/Bytes.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | //
15 | ////////////////////////////////////////////////////////////////////////////////
16 |
17 | package com.google.crypto.tink.subtle;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.security.GeneralSecurityException;
21 | import java.util.Arrays;
22 |
23 | /**
24 | * Helper methods that deal with byte arrays.
25 | *
26 | * @since 1.0.0
27 | */
28 | public final class Bytes {
29 | /**
30 | * Best effort fix-timing array comparison.
31 | *
32 | * @return true if two arrays are equal.
33 | */
34 | public static final boolean equal(final byte[] x, final byte[] y) {
35 | if (x == null || y == null) {
36 | return false;
37 | }
38 | if (x.length != y.length) {
39 | return false;
40 | }
41 | int res = 0;
42 | for (int i = 0; i < x.length; i++) {
43 | res |= x[i] ^ y[i];
44 | }
45 | return res == 0;
46 | }
47 |
48 | /**
49 | * Returns the concatenation of the input arrays in a single array. For example, {@code concat(new
50 | * byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, c}}.
51 | *
52 | * @return a single array containing all the values from the source arrays, in order
53 | */
54 | public static byte[] concat(byte[]... chunks) throws GeneralSecurityException {
55 | int length = 0;
56 | for (byte[] chunk : chunks) {
57 | if (length > Integer.MAX_VALUE - chunk.length) {
58 | throw new GeneralSecurityException("exceeded size limit");
59 | }
60 | length += chunk.length;
61 | }
62 | byte[] res = new byte[length];
63 | int pos = 0;
64 | for (byte[] chunk : chunks) {
65 | System.arraycopy(chunk, 0, res, pos, chunk.length);
66 | pos += chunk.length;
67 | }
68 | return res;
69 | }
70 |
71 | /**
72 | * Computes the xor of two byte arrays, specifying offsets and the length to xor.
73 | *
74 | * @return a new byte[] of length len.
75 | */
76 | public static final byte[] xor(
77 | final byte[] x, int offsetX, final byte[] y, int offsetY, int len) {
78 | if (len < 0 || x.length - len < offsetX || y.length - len < offsetY) {
79 | throw new IllegalArgumentException(
80 | "That combination of buffers, offsets and length to xor result in out-of-bond accesses.");
81 | }
82 | byte[] res = new byte[len];
83 | for (int i = 0; i < len; i++) {
84 | res[i] = (byte) (x[i + offsetX] ^ y[i + offsetY]);
85 | }
86 | return res;
87 | }
88 |
89 | /**
90 | * Computes the xor of two byte buffers, specifying the length to xor, and
91 | * stores the result to {@code output}.
92 | *
93 | * @return a new byte[] of length len.
94 | */
95 | public static final void xor(ByteBuffer output, ByteBuffer x, ByteBuffer y, int len) {
96 | if (len < 0 || x.remaining() < len || y.remaining() < len || output.remaining() < len) {
97 | throw new IllegalArgumentException(
98 | "That combination of buffers, offsets and length to xor result in out-of-bond accesses.");
99 | }
100 | for (int i = 0; i < len; i++) {
101 | output.put((byte) (x.get() ^ y.get()));
102 | }
103 | }
104 |
105 | /**
106 | * Computes the xor of two byte arrays of equal size.
107 | *
108 | * @return a new byte[] of length x.length.
109 | */
110 | public static final byte[] xor(final byte[] x, final byte[] y) {
111 | if (x.length != y.length) {
112 | throw new IllegalArgumentException("The lengths of x and y should match.");
113 | }
114 | return xor(x, 0, y, 0, x.length);
115 | }
116 |
117 | /**
118 | * xors b to the end of a.
119 | *
120 | * @return a new byte[] of length x.length.
121 | */
122 | public static final byte[] xorEnd(final byte[] a, final byte[] b) {
123 | if (a.length < b.length) {
124 | throw new IllegalArgumentException("xorEnd requires a.length >= b.length");
125 | }
126 | int paddingLength = a.length - b.length;
127 | byte[] res = Arrays.copyOf(a, a.length);
128 | for (int i = 0; i < b.length; i++) {
129 | res[paddingLength + i] ^= b[i];
130 | }
131 | return res;
132 | }
133 |
134 | // TODO(thaidn): add checks for boundary conditions/overflows.
135 |
136 | /**
137 | * Transforms a passed value to a LSB first byte array with the size of the specified capacity
138 | *
139 | * @param capacity size of the resulting byte array
140 | * @param value that should be represented as a byte array
141 | */
142 | public static byte[] intToByteArray(int capacity, int value) {
143 | final byte[] result = new byte[capacity];
144 | for (int i = 0; i < capacity; i++) {
145 | result[i] = (byte) ((value >> (8 * i)) & 0xFF);
146 | }
147 | return result;
148 | }
149 |
150 | /**
151 | * Transforms a passed LSB first byte array to an int
152 | *
153 | * @param bytes that should be transformed to a byte array
154 | */
155 | public static int byteArrayToInt(byte[] bytes) {
156 | return byteArrayToInt(bytes, bytes.length);
157 | }
158 |
159 | /**
160 | * Transforms a passed LSB first byte array to an int
161 | *
162 | * @param bytes that should be transformed to a byte array
163 | * @param length amount of the passed {@code bytes} that should be transformed
164 | */
165 | public static int byteArrayToInt(byte[] bytes, int length) {
166 | return byteArrayToInt(bytes, 0, length);
167 | }
168 |
169 | /**
170 | * Transforms a passed LSB first byte array to an int
171 | *
172 | * @param bytes that should be transformed to a byte array
173 | * @param offset start index to start the transformation
174 | * @param length amount of the passed {@code bytes} that should be transformed
175 | */
176 | public static int byteArrayToInt(byte[] bytes, int offset, int length) {
177 | int value = 0;
178 | for (int i = 0; i < length; i++) {
179 | value += (bytes[i + offset] & 0xFF) << (i * 8);
180 | }
181 | return value;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519Constants.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | //
15 | ////////////////////////////////////////////////////////////////////////////////
16 |
17 | package com.google.crypto.tink.subtle;
18 |
19 | import java.math.BigInteger;
20 |
21 | /**
22 | * Constants used in {@link Ed25519}.
23 | */
24 | final class Ed25519Constants {
25 |
26 | // d = -121665 / 121666 mod 2^255-19
27 | static final long[] D;
28 | // 2d
29 | static final long[] D2;
30 | // 2^((p-1)/4) mod p where p = 2^255-19
31 | static final long[] SQRTM1;
32 |
33 | /**
34 | * Base point for the Edwards twisted curve = (x, 4/5) and its exponentiations. B_TABLE[i][j] =
35 | * (j+1)*256^i*B for i in [0, 32) and j in [0, 8). Base point B = B_TABLE[0][0]
36 | */
37 | static final Ed25519.CachedXYT[][] B_TABLE;
38 | static final Ed25519.CachedXYT[] B2;
39 |
40 | private static final BigInteger P_BI =
41 | BigInteger.valueOf(2).pow(255).subtract(BigInteger.valueOf(19));
42 | private static final BigInteger D_BI =
43 | BigInteger.valueOf(-121665).multiply(BigInteger.valueOf(121666).modInverse(P_BI)).mod(P_BI);
44 | private static final BigInteger D2_BI = BigInteger.valueOf(2).multiply(D_BI).mod(P_BI);
45 | private static final BigInteger SQRTM1_BI =
46 | BigInteger.valueOf(2).modPow(P_BI.subtract(BigInteger.ONE).divide(BigInteger.valueOf(4)), P_BI);
47 |
48 | private static class Point {
49 | private BigInteger x;
50 | private BigInteger y;
51 | }
52 |
53 | private static BigInteger recoverX(BigInteger y) {
54 | // x^2 = (y^2 - 1) / (d * y^2 + 1) mod 2^255-19
55 | BigInteger xx =
56 | y.pow(2)
57 | .subtract(BigInteger.ONE)
58 | .multiply(D_BI.multiply(y.pow(2)).add(BigInteger.ONE).modInverse(P_BI));
59 | BigInteger x = xx.modPow(P_BI.add(BigInteger.valueOf(3)).divide(BigInteger.valueOf(8)), P_BI);
60 | if (!x.pow(2).subtract(xx).mod(P_BI).equals(BigInteger.ZERO)) {
61 | x = x.multiply(SQRTM1_BI).mod(P_BI);
62 | }
63 | if (x.testBit(0)) {
64 | x = P_BI.subtract(x);
65 | }
66 | return x;
67 | }
68 |
69 | private static Point edwards(Point a, Point b) {
70 | Point o = new Point();
71 | BigInteger xxyy = D_BI.multiply(a.x.multiply(b.x).multiply(a.y).multiply(b.y)).mod(P_BI);
72 | o.x =
73 | (a.x.multiply(b.y).add(b.x.multiply(a.y)))
74 | .multiply(BigInteger.ONE.add(xxyy).modInverse(P_BI))
75 | .mod(P_BI);
76 | o.y =
77 | (a.y.multiply(b.y).add(a.x.multiply(b.x)))
78 | .multiply(BigInteger.ONE.subtract(xxyy).modInverse(P_BI))
79 | .mod(P_BI);
80 | return o;
81 | }
82 |
83 | private static byte[] toLittleEndian(BigInteger n) {
84 | byte[] b = new byte[32];
85 | byte[] nBytes = n.toByteArray();
86 | System.arraycopy(nBytes, 0, b, 32 - nBytes.length, nBytes.length);
87 | for (int i = 0; i < b.length / 2; i++) {
88 | byte t = b[i];
89 | b[i] = b[b.length - i - 1];
90 | b[b.length - i - 1] = t;
91 | }
92 | return b;
93 | }
94 |
95 | private static Ed25519.CachedXYT getCachedXYT(Point p) {
96 | return new Ed25519.CachedXYT(
97 | Field25519.expand(toLittleEndian(p.y.add(p.x).mod(P_BI))),
98 | Field25519.expand(toLittleEndian(p.y.subtract(p.x).mod(P_BI))),
99 | Field25519.expand(toLittleEndian(D2_BI.multiply(p.x).multiply(p.y).mod(P_BI))));
100 | }
101 |
102 | static {
103 | Point b = new Point();
104 | b.y = BigInteger.valueOf(4).multiply(BigInteger.valueOf(5).modInverse(P_BI)).mod(P_BI);
105 | b.x = recoverX(b.y);
106 |
107 | D = Field25519.expand(toLittleEndian(D_BI));
108 | D2 = Field25519.expand(toLittleEndian(D2_BI));
109 | SQRTM1 = Field25519.expand(toLittleEndian(SQRTM1_BI));
110 |
111 | Point bi = b;
112 | B_TABLE = new Ed25519.CachedXYT[32][8];
113 | for (int i = 0; i < 32; i++) {
114 | Point bij = bi;
115 | for (int j = 0; j < 8; j++) {
116 | B_TABLE[i][j] = getCachedXYT(bij);
117 | bij = edwards(bij, bi);
118 | }
119 | for (int j = 0; j < 8; j++) {
120 | bi = edwards(bi, bi);
121 | }
122 | }
123 | bi = b;
124 | Point b2 = edwards(b, b);
125 | B2 = new Ed25519.CachedXYT[8];
126 | for (int i = 0; i < 8; i++) {
127 | B2[i] = getCachedXYT(bi);
128 | bi = edwards(bi, b2);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/BIPProtocol.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | public enum BIPProtocol {
4 |
5 | BIP32,
6 | BIP44
7 | }
8 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/BaseInput.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.Utils;
4 | import io.bytom.offline.types.*;
5 |
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.util.Map;
9 |
10 | public abstract class BaseInput {
11 |
12 | static final int ISSUANCE_INPUT_TYPE = 0;
13 | static final int SPEND_INPUT_TYPE = 1;
14 |
15 | static final byte AssetKeySpace = 0;
16 | static final byte AccountKeySpace = 1;
17 |
18 | private String inputID;
19 |
20 | /**
21 | * The number of units of the asset being issued or spent.
22 | */
23 | private Long amount;
24 |
25 | /**
26 | * The id of the asset being issued or spent.
27 | */
28 | private String assetId;
29 |
30 | /**
31 | * The program which must be satisfied to transfer this output.
32 | * it represents control program when is SpendInput, it represents issuance program when is IssuanceInput
33 | */
34 | private String program;
35 |
36 | private int vmVersion = 1;
37 |
38 | /**
39 | * used for bip32 derived path.
40 | * it represents accountIndex when is SpendInput, it represents assetIndex when is IssuanceInput
41 | */
42 | private Integer keyIndex;
43 |
44 | WitnessComponent witnessComponent = new WitnessComponent();
45 |
46 | public BaseInput() {}
47 |
48 | public abstract InputEntry toInputEntry(Map entryMap, int index);
49 | public abstract void buildWitness(String txID) throws Exception;
50 | public abstract byte[] serializeInputCommitment() throws IOException;
51 | public abstract byte[] serializeInputWitness() throws IOException;
52 |
53 | public byte[] serializeInput() throws IOException {
54 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
55 | // assert version
56 | Utils.writeVarint(1, stream);
57 | Utils.writeExtensibleString(serializeInputCommitment(), stream);
58 | Utils.writeExtensibleString(serializeInputWitness(), stream);
59 | return stream.toByteArray();
60 | }
61 |
62 |
63 | public void validate() {
64 | if (assetId == null) {
65 | throw new IllegalArgumentException("the asset id of input must be specified.");
66 | }
67 | if (amount == null) {
68 | throw new IllegalArgumentException("the amount id of input must be specified.");
69 | }
70 | if (program == null) {
71 | throw new IllegalArgumentException("the program id of input must be specified.");
72 | }
73 | if (keyIndex == null) {
74 | throw new IllegalArgumentException("the key index of input must be specified.");
75 | }
76 | if (witnessComponent.getRootPrivateKey() == null) {
77 | throw new IllegalArgumentException("the root private key of input must be specified.");
78 | }
79 | }
80 |
81 | public AssetAmount getAssetAmount() {
82 | return new AssetAmount(new AssetID(this.assetId), this.amount);
83 | }
84 |
85 | public void setRootPrivateKey(String prvKey) {
86 | witnessComponent.setRootPrivateKey(prvKey);
87 | }
88 |
89 | public String getInputID() {
90 | return inputID;
91 | }
92 |
93 | public void setInputID(String inputID) {
94 | this.inputID = inputID;
95 | }
96 |
97 | public long getAmount() {
98 | return amount;
99 | }
100 |
101 | public void setAmount(long amount) {
102 | this.amount = amount;
103 | }
104 |
105 | public String getAssetId() {
106 | return assetId;
107 | }
108 |
109 | public void setAssetId(String assetId) {
110 | this.assetId = assetId;
111 | }
112 |
113 | public String getProgram() {
114 | return program;
115 | }
116 |
117 | public void setProgram(String program) {
118 | this.program = program;
119 | }
120 |
121 | public int getVmVersion() {
122 | return vmVersion;
123 | }
124 |
125 | public void setVmVersion(int vmVersion) {
126 | this.vmVersion = vmVersion;
127 | }
128 |
129 | public int getKeyIndex() {
130 | return keyIndex;
131 | }
132 |
133 | public void setKeyIndex(int keyIndex) {
134 | this.keyIndex = keyIndex;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/IssuanceInput.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.DerivePrivateKey;
4 | import io.bytom.offline.common.ExpandedPrivateKey;
5 | import io.bytom.offline.common.Signer;
6 | import io.bytom.offline.common.Utils;
7 | import io.bytom.offline.types.*;
8 | import io.bytom.offline.util.SHA3Util;
9 | import org.bouncycastle.util.encoders.Hex;
10 | import java.io.ByteArrayOutputStream;
11 | import java.io.IOException;
12 | import java.security.SecureRandom;
13 | import java.util.Map;
14 |
15 | public class IssuanceInput extends BaseInput {
16 |
17 | private String nonce;
18 |
19 | private String rawAssetDefinition;
20 |
21 | public IssuanceInput() {}
22 |
23 | public IssuanceInput(String assetID, Long amount, String issuanceProgram) {
24 | this.setAssetId(assetID);
25 | this.setAmount(amount);
26 | this.setProgram(issuanceProgram);
27 | }
28 |
29 | public IssuanceInput(String assetID, Long amount, String issuanceProgram, String nonce, String rawAssetDefinition) {
30 | this(assetID, amount, issuanceProgram);
31 | this.nonce = nonce;
32 | this.rawAssetDefinition = rawAssetDefinition;
33 | }
34 |
35 | @Override
36 | public InputEntry toInputEntry(Map entryMap, int index) {
37 | if (this.nonce == null) {
38 | SecureRandom sr = new SecureRandom();
39 | byte[] randBytes = new byte[8];
40 | sr.nextBytes(randBytes);
41 | this.nonce = Hex.toHexString(randBytes);
42 | }
43 |
44 | Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(this.nonce)));
45 | Hash assetDefHash = new Hash(this.rawAssetDefinition);
46 | AssetAmount value = this.getAssetAmount();
47 |
48 | Program program = new Program(this.getVmVersion(), Hex.decode(this.getProgram()));
49 | return new Issue(nonceHash, value, index, new AssetDefinition(assetDefHash, program));
50 | }
51 |
52 | @Override
53 | public void buildWitness(String txID) throws Exception {
54 | String rootPrivateKey = witnessComponent.getRootPrivateKey();
55 | byte[] childPrivateKey = DerivePrivateKey.bip32derivePrvKey(rootPrivateKey, getKeyIndex(), AssetKeySpace);
56 |
57 | byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID));
58 | byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(childPrivateKey);
59 | byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message);
60 |
61 | witnessComponent.appendWitness(Hex.toHexString(sig));
62 | }
63 |
64 | @Override
65 | public byte[] serializeInputCommitment() throws IOException {
66 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
67 | Utils.writeVarint(ISSUANCE_INPUT_TYPE, stream);
68 | Utils.writeVarStr(Hex.decode(nonce), stream);
69 | stream.write(Hex.decode(getAssetId()));
70 | Utils.writeVarint(getAmount(), stream);
71 | return stream.toByteArray();
72 | }
73 |
74 | @Override
75 | public byte[] serializeInputWitness() throws IOException {
76 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
77 | Utils.writeVarStr(Hex.decode(rawAssetDefinition), stream);
78 | // vm version
79 | Utils.writeVarint(1, stream);
80 | Utils.writeVarStr(Hex.decode(getProgram()), stream);
81 | Utils.writeVarList(witnessComponent.toByteArray(), stream);
82 | return stream.toByteArray();
83 | }
84 |
85 | @Override
86 | public void validate() {
87 | super.validate();
88 | if (nonce == null) {
89 | throw new IllegalArgumentException("the nonce of issuance input must be specified.");
90 | }
91 | if (rawAssetDefinition == null) {
92 | throw new IllegalArgumentException("the nonce of issuance input must be specified.");
93 | }
94 | }
95 |
96 | public String getNonce() {
97 | return nonce;
98 | }
99 |
100 | public void setNonce(String nonce) {
101 | this.nonce = nonce;
102 | }
103 |
104 | public String getRawAssetDefinition() {
105 | return rawAssetDefinition;
106 | }
107 |
108 | public void setRawAssetDefinition(String rawAssetDefinition) {
109 | this.rawAssetDefinition = rawAssetDefinition;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/Output.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.Utils;
4 | import io.bytom.offline.common.VMUtil;
5 | import org.bouncycastle.util.encoders.Hex;
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 |
9 | public class Output {
10 |
11 | /**
12 | * The number of units of the asset being controlled.
13 | */
14 | private Long amount;
15 |
16 | /**
17 | * The id of the asset being controlled.
18 | */
19 | private String assetId;
20 |
21 | /**
22 | * The control program which must be satisfied to transfer this output.
23 | */
24 | private String controlProgram;
25 |
26 | /**
27 | * The id of the output.
28 | */
29 | private String id;
30 |
31 | /**
32 | * The output's position in a transaction's list of outputs.
33 | */
34 | private Integer position;
35 |
36 | public Output(String assetID, long amount, String controlProgram) {
37 | this.assetId = assetID;
38 | this.amount = amount;
39 | this.controlProgram = controlProgram;
40 | }
41 |
42 | public static Output newRetireOutput(String assetID, long amount, String arbitrary) {
43 | String retireProgram = Hex.toHexString(new byte[]{VMUtil.OP_FAIL}) + Hex.toHexString(VMUtil.pushDataBytes(Hex.decode(arbitrary)));
44 | return new Output(assetID, amount, retireProgram);
45 | }
46 |
47 | public byte[] serializeOutput() throws IOException {
48 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
49 |
50 | //assetVersion
51 | Utils.writeVarint(1, stream); //AssetVersion是否默认为1
52 | //outputCommit
53 | ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();
54 | //assetId
55 | outputCommitSteam.write(Hex.decode(assetId));
56 | //amount
57 | Utils.writeVarint(amount, outputCommitSteam);
58 | //vmVersion
59 | Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version
60 | //controlProgram
61 | Utils.writeVarStr(Hex.decode(controlProgram), outputCommitSteam);
62 |
63 | Utils.writeExtensibleString(outputCommitSteam.toByteArray(), stream);
64 |
65 | //outputWitness
66 | Utils.writeVarint(0, stream);
67 | return stream.toByteArray();
68 | }
69 |
70 | public Long getAmount() {
71 | return amount;
72 | }
73 |
74 | public void setAmount(Long amount) {
75 | this.amount = amount;
76 | }
77 |
78 | public String getAssetId() {
79 | return assetId;
80 | }
81 |
82 | public void setAssetId(String assetId) {
83 | this.assetId = assetId;
84 | }
85 |
86 | public String getControlProgram() {
87 | return controlProgram;
88 | }
89 |
90 | public void setControlProgram(String controlProgram) {
91 | this.controlProgram = controlProgram;
92 | }
93 |
94 | public String getId() {
95 | return id;
96 | }
97 |
98 | public void setId(String id) {
99 | this.id = id;
100 | }
101 |
102 | public Integer getPosition() {
103 | return position;
104 | }
105 |
106 | public void setPosition(Integer position) {
107 | this.position = position;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/SpendInput.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.*;
4 | import io.bytom.offline.types.*;
5 | import org.bouncycastle.util.encoders.Hex;
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.util.Map;
9 |
10 | public class SpendInput extends BaseInput {
11 |
12 | private String sourceID;
13 |
14 | private Integer sourcePosition;
15 |
16 | private Boolean change;
17 |
18 | private Integer controlProgramIndex;
19 |
20 | private BIPProtocol bipProtocol = BIPProtocol.BIP44;
21 |
22 | public SpendInput() {}
23 |
24 | public SpendInput(String assetID, long amount, String controlProgram) {
25 | this.setAssetId(assetID);
26 | this.setAmount(amount);
27 | this.setProgram(controlProgram);
28 | }
29 |
30 | public SpendInput(String assetID, long amount, String controlProgram, String sourceID, Integer sourcePosition) {
31 | this(assetID, amount, controlProgram);
32 | this.sourceID = sourceID;
33 | this.sourcePosition = sourcePosition;
34 | }
35 |
36 | @Override
37 | public InputEntry toInputEntry(Map entryMap, int index) {
38 | Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram()));
39 | AssetAmount assetAmount = this.getAssetAmount();
40 | Hash sourceID = new Hash(this.sourceID);
41 | ValueSource src = new ValueSource(sourceID, assetAmount, this.sourcePosition);
42 |
43 | OutputEntry prevOut = new OutputEntry(src, pro, 0);
44 | Hash prevOutID = prevOut.entryID();
45 | entryMap.put(prevOutID, prevOut);
46 | return new Spend(prevOutID, index);
47 | }
48 |
49 | @Override
50 | public void buildWitness(String txID) throws Exception {
51 | String rootPrvKey = witnessComponent.getRootPrivateKey();
52 |
53 | byte[] privateChild;
54 | if (bipProtocol == BIPProtocol.BIP44) {
55 | privateChild = DerivePrivateKey.bip44derivePrvKey(rootPrvKey, getKeyIndex(), change, controlProgramIndex);
56 | } else {
57 | privateChild = DerivePrivateKey.bip32derivePrvKey(rootPrvKey, getKeyIndex(), AccountKeySpace, controlProgramIndex);
58 | }
59 |
60 | byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID));
61 | byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(privateChild);
62 | byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message);
63 |
64 | witnessComponent.appendWitness(Hex.toHexString(sig));
65 |
66 | byte[] deriveXpub = DeriveXpub.deriveXpub(privateChild);
67 | String pubKey = Hex.toHexString(deriveXpub).substring(0, 64);
68 |
69 | witnessComponent.appendWitness(pubKey);
70 | }
71 |
72 | @Override
73 | public byte[] serializeInputCommitment() throws IOException {
74 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
75 | Utils.writeVarint(SPEND_INPUT_TYPE, stream);
76 |
77 | ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();
78 | spendCommitSteam.write(Hex.decode(sourceID));
79 | spendCommitSteam.write(Hex.decode(getAssetId()));
80 | Utils.writeVarint(getAmount(), spendCommitSteam);
81 | Utils.writeVarint(sourcePosition, spendCommitSteam);
82 | // vm version
83 | Utils.writeVarint(1, spendCommitSteam);
84 | Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam);
85 | Utils.writeExtensibleString(spendCommitSteam.toByteArray(), stream);
86 | return stream.toByteArray();
87 | }
88 |
89 | @Override
90 | public byte[] serializeInputWitness() throws IOException {
91 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
92 | Utils.writeVarList(witnessComponent.toByteArray(), stream);
93 | return stream.toByteArray();
94 | }
95 |
96 | @Override
97 | public void validate() {
98 | super.validate();
99 | if (sourceID == null) {
100 | throw new IllegalArgumentException("the source id of spend input must be specified.");
101 | }
102 | if (sourcePosition == null) {
103 | throw new IllegalArgumentException("the source position of spend input must be specified.");
104 | }
105 | if (change == null) {
106 | throw new IllegalArgumentException("the change of spend input must be specified.");
107 | }
108 | if (controlProgramIndex == null) {
109 | throw new IllegalArgumentException("the control program index of spend input must be specified.");
110 | }
111 | }
112 |
113 | public void setSourceID(String sourceID) {
114 | this.sourceID = sourceID;
115 | }
116 |
117 | public void setSourcePosition(int sourcePosition) {
118 | this.sourcePosition = sourcePosition;
119 | }
120 |
121 | public void setChange(boolean change) {
122 | this.change = change;
123 | }
124 |
125 | public void setControlProgramIndex(int controlProgramIndex) {
126 | this.controlProgramIndex = controlProgramIndex;
127 | }
128 |
129 | public void setBipProtocol(BIPProtocol bipProtocol) {
130 | this.bipProtocol = bipProtocol;
131 | }
132 | }
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/Transaction.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.Utils;
4 | import io.bytom.offline.exception.MapTransactionException;
5 | import io.bytom.offline.exception.SerializeTransactionException;
6 | import io.bytom.offline.exception.SignTransactionException;
7 | import io.bytom.offline.types.*;
8 | import org.bouncycastle.util.encoders.Hex;
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.IOException;
11 | import java.util.ArrayList;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | /**
17 | * Created by liqiang on 2018/10/24.
18 | */
19 |
20 | public class Transaction {
21 |
22 | private String txID;
23 | /**
24 | * version
25 | */
26 | private Integer version;
27 | /**
28 | * size
29 | */
30 | private Integer size;
31 | /**
32 | * time_range
33 | */
34 | private Integer timeRange;
35 |
36 | /**
37 | * List of specified inputs for a transaction.
38 | */
39 | private List inputs;
40 |
41 | /**
42 | * List of specified outputs for a transaction.
43 | */
44 | private List outputs;
45 |
46 | public Transaction(Builder builder) {
47 | this.inputs = builder.inputs;
48 | this.outputs = builder.outputs;
49 | this.version = builder.version;
50 | this.size = builder.size;
51 | this.timeRange = builder.timeRange;
52 |
53 | this.validate();
54 | this.mapTx();
55 | this.sign();
56 | }
57 |
58 | public static class Builder {
59 |
60 | private Integer version = 1;
61 |
62 | private Integer size = 0;
63 |
64 | private Integer timeRange;
65 |
66 | private List inputs;
67 | private List outputs;
68 |
69 | public Builder() {
70 | this.inputs = new ArrayList<>();
71 | this.outputs = new ArrayList<>();
72 | }
73 |
74 | public Builder addInput(BaseInput input) {
75 | this.inputs.add(input);
76 | return this;
77 | }
78 |
79 | public Builder addOutput(Output output) {
80 | this.outputs.add(output);
81 | return this;
82 | }
83 |
84 | public Builder setTimeRange(int timeRange) {
85 | this.timeRange = timeRange;
86 | return this;
87 | }
88 |
89 | public Transaction build() {
90 | return new Transaction(this);
91 | }
92 | }
93 |
94 | private void sign() {
95 | for (BaseInput input : inputs) {
96 | try {
97 | input.buildWitness(txID);
98 | } catch (Exception e) {
99 | e.printStackTrace();
100 | throw new SignTransactionException(e);
101 | }
102 | }
103 | }
104 |
105 | public String rawTransaction() {
106 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
107 | try {
108 | stream.write(7);
109 |
110 | Utils.writeVarint(version, stream);
111 |
112 | Utils.writeVarint(timeRange, stream);
113 |
114 | Utils.writeVarint(inputs.size(), stream);
115 | for (BaseInput input : inputs) {
116 | stream.write(input.serializeInput());
117 | }
118 |
119 | Utils.writeVarint(outputs.size(), stream);
120 | for (Output output : outputs) {
121 | stream.write(output.serializeOutput());
122 | }
123 | } catch (IOException e) {
124 | e.printStackTrace();
125 | throw new SerializeTransactionException(e);
126 | }
127 | return Hex.toHexString(stream.toByteArray());
128 | }
129 |
130 | private void validate() {
131 | if (version == null) {
132 | throw new IllegalArgumentException("the version of transaction must be specified.");
133 | }
134 | if (timeRange == null) {
135 | throw new IllegalArgumentException("the time range of transaction must be specified.");
136 | }
137 | if (size == null) {
138 | throw new IllegalArgumentException("the size range of transaction must be specified.");
139 | }
140 |
141 | for (BaseInput input : inputs) {
142 | input.validate();
143 | }
144 | }
145 |
146 | private void mapTx() {
147 | Map entryMap = new HashMap<>();
148 | ValueSource[] muxSources = new ValueSource[inputs.size()];
149 | List inputEntries = new ArrayList<>();
150 |
151 | try {
152 | for (int i = 0; i < inputs.size(); i++) {
153 | BaseInput input = inputs.get(i);
154 | InputEntry inputEntry = input.toInputEntry(entryMap, i);
155 | Hash spendID = addEntry(entryMap, inputEntry);
156 | input.setInputID(spendID.toString());
157 |
158 | muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0);
159 | inputEntries.add(inputEntry);
160 | }
161 |
162 | Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));
163 | Hash muxID = addEntry(entryMap, mux);
164 | for (InputEntry inputEntry : inputEntries) {
165 | inputEntry.setDestination(muxID, inputEntry.getOrdinal(), entryMap);
166 | }
167 |
168 | List resultIDList = new ArrayList<>();
169 | for (int i = 0; i < outputs.size(); i++) {
170 | Output output = outputs.get(i);
171 |
172 | AssetAmount amount = new AssetAmount(new AssetID(output.getAssetId()), output.getAmount());
173 | ValueSource src = new ValueSource(muxID, amount, i);
174 |
175 | Hash resultID;
176 | if (output.getControlProgram().startsWith("6a")) {
177 | Retirement retirement = new Retirement(src, i);
178 | resultID = addEntry(entryMap, retirement);
179 | } else {
180 | Program program = new Program(1, Hex.decode(output.getControlProgram()));
181 | OutputEntry oup = new OutputEntry(src, program, i);
182 | resultID = addEntry(entryMap, oup);
183 | }
184 |
185 | resultIDList.add(resultID);
186 | output.setId(resultID.toString());
187 |
188 | ValueDestination destination = new ValueDestination(resultID, src.getValue(), 0);
189 | mux.getWitnessDestinations().add(destination);
190 | }
191 |
192 | TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{}));
193 | Hash txID = addEntry(entryMap, txHeader);
194 | this.txID = txID.toString();
195 |
196 | } catch (Exception e) {
197 | e.printStackTrace();
198 | throw new MapTransactionException(e);
199 | }
200 | }
201 |
202 | private Hash addEntry(Map entryMap, Entry entry) {
203 | Hash id = entry.entryID();
204 | entryMap.put(id, entry);
205 | return id;
206 | }
207 |
208 | public String getTxID() {
209 | return txID;
210 | }
211 |
212 | public Integer getVersion() {
213 | return version;
214 | }
215 |
216 | public Integer getSize() {
217 | return size;
218 | }
219 |
220 | public Integer getTimeRange() {
221 | return timeRange;
222 | }
223 |
224 | public List getInputs() {
225 | return inputs;
226 | }
227 |
228 | public List getOutputs() {
229 | return outputs;
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/api/WitnessComponent.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * A single witness component, holding information that will become the input
10 | * witness.
11 | */
12 | public class WitnessComponent {
13 |
14 | /**
15 | * The list of witnesses made with the specified keys (null unless type is
16 | * "signature").
17 | */
18 | private List witnesses;
19 |
20 | private String rootPrivateKey;
21 |
22 | public WitnessComponent() {
23 | witnesses = new ArrayList<>();
24 | }
25 |
26 | public byte[][] toByteArray() {
27 | byte[][] byteArray = new byte[witnesses.size()][];
28 | for (int i = 0; i < witnesses.size(); i++) {
29 | byteArray[i] = Hex.decode(witnesses.get(i));
30 | }
31 | return byteArray;
32 | }
33 |
34 | public String getWitness(int index) {
35 | return witnesses.get(index);
36 | }
37 |
38 | public void appendWitness(String witness) {
39 | witnesses.add(witness);
40 | }
41 |
42 | public String getRootPrivateKey() {
43 | return rootPrivateKey;
44 | }
45 |
46 | public void setRootPrivateKey(String rootPrivateKey) {
47 | this.rootPrivateKey = rootPrivateKey;
48 | }
49 | }
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/DerivePrivateKey.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import io.bytom.offline.util.PathUtil;
4 | import org.bouncycastle.util.encoders.Hex;
5 |
6 | import java.security.InvalidKeyException;
7 | import java.security.NoSuchAlgorithmException;
8 | import java.security.SignatureException;
9 |
10 | public class DerivePrivateKey {
11 | //bip44 派生子秘钥
12 | //accountIndex 默认为1
13 | public static byte[] bip44derivePrvKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
14 | byte[][] paths = PathUtil.getBip44Path(accountIndex, change, programIndex);
15 | byte[] res = Hex.decode(rootPriv);
16 | for (int i = 0; i < paths.length; i++) {
17 | byte[] xpub = DeriveXpub.deriveXpub(res);
18 | res = NonHardenedChild.nhChild(paths[i], res, xpub);
19 | }
20 | return res;
21 | }
22 |
23 | //BIP32 派生子秘钥 issue 需要用到BIP32派生规则
24 | public static byte[] bip32derivePrvKey(String rootPrivateKey, int accountIndex, byte keySpace, long ...programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
25 | byte[] res = Hex.decode(rootPrivateKey);
26 | byte[][] paths = PathUtil.getBip32Path(keySpace, accountIndex, programIndex);
27 | for (int i = 0; i < paths.length; i++) {
28 | byte[] xpub = DeriveXpub.deriveXpub(res);
29 | res = NonHardenedChild.nhChild(paths[i], res, xpub);
30 | }
31 | return res;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/DeriveXpub.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import com.google.crypto.tink.subtle.Ed25519;
4 |
5 | public class DeriveXpub {
6 | public static byte[] deriveXpub(byte[] xprv) {
7 | byte[] xpub = new byte[xprv.length];
8 | byte[] scalar = new byte[xprv.length / 2];
9 |
10 | System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2);
11 | byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar);
12 |
13 | System.arraycopy(buf, 0, xpub, 0, buf.length);
14 | System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2);
15 | return xpub;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/ExpandedPrivateKey.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 | import javax.crypto.Mac;
5 | import javax.crypto.spec.SecretKeySpec;
6 | import java.security.InvalidKeyException;
7 | import java.security.NoSuchAlgorithmException;
8 |
9 | public class ExpandedPrivateKey {
10 | public static byte[] hmacSha512(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
11 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");
12 | Mac mac = Mac.getInstance("HmacSHA512");
13 | mac.init(signingKey);
14 | return mac.doFinal(data);
15 | }
16 |
17 | public static byte[] expandedPrivateKey(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException {
18 | // "457870616e64" is "Expand" hex.
19 | byte[] res = hmacSha512(data, Hex.decode("457870616e64"));
20 | for (int i = 0; i <= 31; i++) {
21 | res[i] = data[i];
22 | }
23 | return res;
24 | }
25 | }
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/NonHardenedChild.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 |
5 | import javax.crypto.Mac;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import java.io.ByteArrayOutputStream;
8 | import java.security.InvalidKeyException;
9 | import java.security.NoSuchAlgorithmException;
10 |
11 | public class NonHardenedChild {
12 |
13 | private static byte[] hmacSha512(byte[] data, byte[] key)
14 | throws NoSuchAlgorithmException, InvalidKeyException {
15 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");
16 | Mac mac = Mac.getInstance("HmacSHA512");
17 | mac.init(signingKey);
18 | return mac.doFinal(data);
19 | }
20 |
21 | public static byte[] nhChild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, InvalidKeyException {
22 | // begin build data
23 | ByteArrayOutputStream out = new ByteArrayOutputStream();
24 | out.write('N');
25 | out.write(xpub, 0, xpub.length / 2);
26 | out.write(path, 0, path.length);
27 | byte[] data = out.toByteArray();
28 | // end build data
29 |
30 | // begin build key
31 | byte[] key = new byte[xpub.length / 2];
32 | System.arraycopy(xpub, xpub.length / 2, key, 0, xpub.length / 2);
33 | // end build key
34 |
35 | // doFinal()
36 | byte[] res = hmacSha512(data, key);
37 |
38 | //begin operate res[:32]
39 | byte[] f = new byte[res.length / 2];
40 | System.arraycopy(res, 0, f, 0, res.length / 2);
41 | f = pruneIntermediateScalar(f);
42 | System.arraycopy(f, 0, res, 0, res.length / 2);
43 | //end operate res[:32]
44 |
45 | //begin operate res[:32] again
46 | int carry = 0;
47 | int sum = 0;
48 | for (int i = 0; i < 32; i++) {
49 | int xprvInt = xprv[i] & 0xFF;
50 | int resInt = res[i] & 0xFF;
51 | sum = xprvInt + resInt + carry;
52 | res[i] = (byte) sum;
53 | carry = sum >> 8;
54 | }
55 | if ((sum >> 8) != 0) {
56 | System.err.println("sum does not fit in 256-bit int");
57 | }
58 | //end operate res[:32] again
59 | return res;
60 | }
61 |
62 | private static byte[] pruneIntermediateScalar(byte[] f) {
63 | f[0] &= 248; // clear bottom 3 bits
64 | f[29] &= 1; // clear 7 high bits
65 | f[30] = 0; // clear 8 bits
66 | f[31] = 0; // clear 8 bits
67 | return f;
68 | }
69 |
70 | public static byte[] child(byte[] xprv, String[] hpaths) throws NoSuchAlgorithmException, InvalidKeyException {
71 | byte[][] paths = new byte[][]{
72 | Hex.decode(hpaths[0]),
73 | Hex.decode(hpaths[1])
74 | };
75 | byte[] res = xprv;
76 | for (int i = 0; i < hpaths.length; i++) {
77 | byte[] xpub = DeriveXpub.deriveXpub(res);
78 | res = NonHardenedChild.nhChild(paths[i], res, xpub);
79 | }
80 | return res;
81 | }
82 | }
83 |
84 |
85 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/Signer.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 | import java.util.Arrays;
6 |
7 | public class Signer {
8 |
9 | public static byte[] ed25519InnerSign(byte[] privateKey, byte[] message) throws NoSuchAlgorithmException {
10 | MessageDigest md = MessageDigest.getInstance("SHA-512");
11 | byte[] digestData = new byte[32 + message.length];
12 | int digestDataIndex = 0;
13 | for (int i = 32; i < 64; i++) {
14 | digestData[digestDataIndex] = privateKey[i];
15 | digestDataIndex++;
16 | }
17 | for (int i = 0; i < message.length; i++) {
18 | digestData[digestDataIndex] = message[i];
19 | digestDataIndex++;
20 | }
21 | md.update(digestData);
22 | byte[] messageDigest = md.digest();
23 |
24 | com.google.crypto.tink.subtle.Ed25519.reduce(messageDigest);
25 | byte[] messageDigestReduced = Arrays.copyOfRange(messageDigest, 0, 32);
26 | byte[] encodedR = com.google.crypto.tink.subtle.Ed25519.scalarMultWithBaseToBytes(messageDigestReduced);
27 | byte[] publicKey = DeriveXpub.deriveXpub(privateKey);
28 |
29 | byte[] hramDigestData = new byte[32 + encodedR.length + message.length];
30 | int hramDigestIndex = 0;
31 | for (int i = 0; i < encodedR.length; i++) {
32 | hramDigestData[hramDigestIndex] = encodedR[i];
33 | hramDigestIndex++;
34 | }
35 | for (int i = 0; i < 32; i++) {
36 | hramDigestData[hramDigestIndex] = publicKey[i];
37 | hramDigestIndex++;
38 | }
39 | for (int i = 0; i < message.length; i++) {
40 | hramDigestData[hramDigestIndex] = message[i];
41 | hramDigestIndex++;
42 | }
43 | md.reset();
44 | md.update(hramDigestData);
45 | byte[] hramDigest = md.digest();
46 | com.google.crypto.tink.subtle.Ed25519.reduce(hramDigest);
47 | byte[] hramDigestReduced = Arrays.copyOfRange(hramDigest, 0, 32);
48 |
49 | byte[] sk = Arrays.copyOfRange(privateKey, 0, 32);
50 | byte[] s = new byte[32];
51 | com.google.crypto.tink.subtle.Ed25519.mulAdd(s, hramDigestReduced, sk, messageDigestReduced);
52 |
53 | byte[] signature = new byte[64];
54 | for (int i = 0; i < encodedR.length; i++) {
55 | signature[i] = encodedR[i];
56 | }
57 | int signatureIndex = 32;
58 | for (int i = 0; i < s.length; i++) {
59 | signature[signatureIndex] = s[i];
60 | signatureIndex++;
61 | }
62 | return signature;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/Utils.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import io.bytom.offline.types.ExpandedKeys;
6 | import org.bouncycastle.jcajce.provider.digest.SHA3;
7 | import org.bouncycastle.util.encoders.Hex;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.IOException;
11 | import java.math.BigInteger;
12 | import java.security.InvalidKeyException;
13 | import java.security.NoSuchAlgorithmException;
14 | import java.security.SignatureException;
15 | import java.util.Arrays;
16 |
17 | public class Utils {
18 |
19 | public static String rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
20 |
21 | public static final Gson serializer = new GsonBuilder().setDateFormat(rfc3339DateFormat).create();
22 |
23 | public static int writeVarint(long value, ByteArrayOutputStream stream) throws IOException {
24 | byte[] varint = new byte[9];
25 | int n = putUvarint(varint, value);
26 | byte[] varintTime = Arrays.copyOf(varint, n);
27 | stream.write(varintTime);
28 | return n;
29 | }
30 |
31 |
32 | public static int writeVarStr(byte[] buf, ByteArrayOutputStream stream) throws IOException {
33 | int n = writeVarint(buf.length, stream);
34 | stream.write(buf);
35 |
36 | return n + (buf.length);
37 | }
38 |
39 | public static int writeVarList(byte[][] list, ByteArrayOutputStream stream) throws IOException {
40 | int n = writeVarint(list.length, stream);
41 | for (byte[] bytes : list) {
42 | n += writeVarStr(bytes, stream);
43 | }
44 | return n;
45 | }
46 |
47 | public static int writeExtensibleString(byte[] bytes, ByteArrayOutputStream stream) throws IOException {
48 | int n = writeVarint(bytes.length, stream);
49 | stream.write(bytes);
50 | return n + bytes.length;
51 | }
52 |
53 | public static int getLengthVarInt(long x) {
54 | byte[] varint = new byte[9];
55 | int n = putUvarint(varint, x);
56 | byte[] varintTime = Arrays.copyOf(varint, n);
57 | return varintTime.length;
58 | }
59 |
60 | private static int putUvarint(byte[] buf, long x) {
61 | int i = 0;
62 | while (x >= 0x80) {
63 | buf[i] = (byte) (x | 0x80);
64 | x >>= 7;
65 | i++;
66 | }
67 | buf[i] = (byte) x;
68 | return i + 1;
69 | }
70 |
71 | public static byte[] BigIntegerToBytes(BigInteger value) {
72 | if (value == null) {
73 | return null;
74 | } else {
75 | byte[] data = value.toByteArray();
76 | if (data.length != 1 && data[0] == 0) {
77 | byte[] tmp = new byte[data.length - 1];
78 | System.arraycopy(data, 1, tmp, 0, tmp.length);
79 | data = tmp;
80 | }
81 |
82 | return data;
83 | }
84 | }
85 |
86 | public static byte[] pruneIntermediateScalar(byte[] f) {
87 | f[0] &= 248;
88 | f[31] &= 31; // clear top 3 bits
89 | f[31] |= 64; // set second highest bit
90 | return f;
91 | }
92 |
93 | public static ExpandedKeys expandedPriKey(String priKey, String key) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
94 | byte[] hashPriKey = ExpandedPrivateKey.hmacSha512(Hex.decode(priKey), key.getBytes());
95 | //begin operate res[:32]
96 | byte[] f = new byte[hashPriKey.length / 2];
97 | System.arraycopy(hashPriKey, 0, f, 0, hashPriKey.length / 2);
98 | f = pruneIntermediateScalar(f);
99 | System.arraycopy(f, 0, hashPriKey, 0, hashPriKey.length / 2);
100 | //end operate res[:32]
101 | byte[] hashPubKey = DeriveXpub.deriveXpub(hashPriKey);
102 | ExpandedKeys expandedKeys = new ExpandedKeys();
103 | expandedKeys.setPriKey(Hex.toHexString(hashPriKey));
104 | expandedKeys.setPubKey(Hex.toHexString(hashPubKey));
105 | return expandedKeys;
106 | }
107 |
108 | public static String pushDataInt(int n) {
109 | if (n==0){
110 | return "00";
111 | }else if (n>=1&&n<=15){
112 | return "5"+Integer.toString(n,16);
113 | }else if(n==16){
114 | return "60";
115 | }
116 | return null;
117 |
118 | }
119 |
120 | public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) {
121 |
122 | SHA3.Digest256 digest256 = new SHA3.Digest256();
123 | // data = hashedInputHex + txID
124 | ByteArrayOutputStream out = new ByteArrayOutputStream();
125 | out.write(hashedInputHex, 0, hashedInputHex.length);
126 | out.write(txID, 0, txID.length);
127 | byte[] data = out.toByteArray();
128 | return digest256.digest(data);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/common/VMUtil.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 |
6 | public class VMUtil {
7 |
8 | public static final byte OP_0 = (byte) 0x00;
9 | public static final byte OP_1 = (byte) 0x51;
10 | public static final byte OP_PUSHDATA1 = (byte) 0x4c;
11 | public static final byte OP_PUSHDATA2 = (byte) 0x4d;
12 | public static final byte OP_PUSHDATA4 = (byte) 0x43;
13 | public static final byte OP_TXSIGHASH = (byte) 0xae;
14 | public static final byte OP_CHECKMULTISIG = (byte) 0xad;
15 | public static final byte OP_FAIL = (byte) 0x6a;
16 |
17 | public static byte[] p2spMultiSigProgram(byte[][] pubKeys, int nRequired) {
18 | checkMultiSigParams(nRequired, pubKeys.length);
19 | ByteArrayOutputStream program = new ByteArrayOutputStream();
20 | program.write(OP_TXSIGHASH);
21 |
22 | for (byte[] pubKey : pubKeys) {
23 | try {
24 | program.write(pushDataBytes(pubKey));
25 | program.write(pushDataInt64(nRequired));
26 | program.write(pushDataInt64(pubKeys.length));
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 | program.write(OP_CHECKMULTISIG);
32 | return program.toByteArray();
33 | }
34 |
35 | private static void checkMultiSigParams(int nRequired, int nPubkeys) {
36 | if (nRequired < 0 || nPubkeys < 0 || nRequired > nPubkeys || (nRequired == 0 && nPubkeys > 0)) {
37 | throw new IllegalArgumentException();
38 | }
39 | }
40 |
41 | public static byte[] pushDataBytes(byte[] data) {
42 | int len = data.length;
43 | if (len == 0) {
44 | return new byte[] {OP_0};
45 | }
46 | if (len <= 75) {
47 | byte[] dest = new byte[1 + len];
48 | dest[0] = (byte) len;
49 | System.arraycopy(data, 0, dest, 1, len);
50 | return dest;
51 | }
52 | if (len < 256) {
53 | byte[] dest = new byte[2 + len];
54 | dest[0] = OP_PUSHDATA1;
55 | dest[1] = (byte) len;
56 | System.arraycopy(data, 0, dest, 2, len);
57 | return dest;
58 | }
59 | if (len < 65536) {
60 | byte[] dest = new byte[3 + len];
61 | dest[0] = OP_PUSHDATA2;
62 | dest[1] = (byte) len;
63 | dest[2] = (byte) (len >> 8);
64 | System.arraycopy(data, 0, dest, 3, len);
65 | return dest;
66 | }
67 | byte[] dest = new byte[5 + len];
68 | dest[0] = OP_PUSHDATA4;
69 | dest[1] = (byte) len;
70 | dest[2] = (byte) (len >> 8);
71 | dest[3] = (byte) (len >> 16);
72 | dest[4] = (byte) (len >> 24);
73 | System.arraycopy(data, 0, dest, 5, len);
74 | return dest;
75 | }
76 |
77 | private static byte[] pushDataInt64(long n) {
78 | if (n == 0) {
79 | return new byte[] {OP_0};
80 | }
81 | if (n >= 1 && n <= 16) {
82 | return new byte[] {(byte) (OP_1 + (byte) n - 1)};
83 | }
84 | return pushDataBytes(int64Bytes(n));
85 | }
86 |
87 | private static byte[] int64Bytes(long n) {
88 | byte[] bytes = new byte[8];
89 | int i = 0;
90 | while (n != 0) {
91 | bytes[i] = (byte) n;
92 | n >>= 8;
93 | i++;
94 | }
95 | byte[] res = new byte[i];
96 | System.arraycopy(bytes, 0, res, 0, i);
97 | return res;
98 | }
99 |
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/exception/MapTransactionException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.exception;
2 |
3 | public class MapTransactionException extends RuntimeException {
4 |
5 | public MapTransactionException() {
6 | super();
7 | }
8 |
9 | public MapTransactionException(String message) {
10 | super(message);
11 | }
12 |
13 | public MapTransactionException(String message, Throwable cause) {
14 | super(message, cause);
15 | }
16 |
17 |
18 | public MapTransactionException(Throwable cause) {
19 | super(cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/exception/SerializeTransactionException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.exception;
2 |
3 | public class SerializeTransactionException extends RuntimeException {
4 |
5 | public SerializeTransactionException() {
6 | super();
7 | }
8 |
9 | public SerializeTransactionException(String message) {
10 | super(message);
11 | }
12 |
13 | public SerializeTransactionException(String message, Throwable cause) {
14 | super(message, cause);
15 | }
16 |
17 |
18 | public SerializeTransactionException(Throwable cause) {
19 | super(cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/exception/SignTransactionException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.exception;
2 |
3 | public class SignTransactionException extends RuntimeException {
4 |
5 | public SignTransactionException() {
6 | super();
7 | }
8 |
9 | public SignTransactionException(String message) {
10 | super(message);
11 | }
12 |
13 | public SignTransactionException(String message, Throwable cause) {
14 | super(message, cause);
15 | }
16 |
17 |
18 | public SignTransactionException(Throwable cause) {
19 | super(cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/exception/WriteForHashException.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.exception;
2 |
3 | public class WriteForHashException extends RuntimeException {
4 |
5 | public WriteForHashException() {
6 | super();
7 | }
8 |
9 | public WriteForHashException(String message) {
10 | super(message);
11 | }
12 |
13 | public WriteForHashException(String message, Throwable cause) {
14 | super(message, cause);
15 | }
16 |
17 |
18 | public WriteForHashException(Throwable cause) {
19 | super(cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/AssetAmount.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | public class AssetAmount {
4 |
5 | private AssetID assetID;
6 |
7 | private Long amount;
8 |
9 | public AssetAmount() {}
10 |
11 | public AssetAmount(AssetID assetID, long amount) {
12 | this.assetID = assetID;
13 | this.amount = amount;
14 | }
15 |
16 | public AssetID getAssetID() {
17 | return assetID;
18 | }
19 |
20 | public void setAssetID(AssetID assetID) {
21 | this.assetID = assetID;
22 | }
23 |
24 | public Long getAmount() {
25 | return amount;
26 | }
27 |
28 | public void setAmount(Long amount) {
29 | this.amount = amount;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/AssetDefinition.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 |
5 | public class AssetDefinition extends Entry {
6 |
7 | private Hash assetDefHash;
8 | private Program program;
9 |
10 | public AssetDefinition(Hash assetDefHash, Program program) {
11 | this.assetDefHash = assetDefHash;
12 | this.program = program;
13 | }
14 |
15 | @Override
16 | public String typ() {
17 | return "asset";
18 | }
19 |
20 | @Override
21 | public void writeForHash(ByteArrayOutputStream out) {
22 | mustWriteForHash(out, this.program);
23 | mustWriteForHash(out, this.assetDefHash);
24 | }
25 |
26 | public Hash getAssetDefHash() {
27 | return assetDefHash;
28 | }
29 |
30 | public Program getProgram() {
31 | return program;
32 | }
33 |
34 | public void setAssetDefHash(Hash assetDefHash) {
35 | this.assetDefHash = assetDefHash;
36 | }
37 |
38 | public void setProgram(Program program) {
39 | this.program = program;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/AssetID.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 |
5 | public class AssetID {
6 |
7 | private String hexValue;
8 |
9 | public AssetID() {}
10 |
11 | public AssetID(String hexValue) {
12 | this.hexValue = hexValue;
13 | }
14 |
15 |
16 | public AssetID(byte[] byteArray) {
17 | this.hexValue = Hex.toHexString(byteArray);
18 | }
19 |
20 | public byte[] toByteArray() {
21 | return Hex.decode(this.hexValue);
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Entry.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import io.bytom.offline.exception.WriteForHashException;
4 | import io.bytom.offline.util.OutputUtil;
5 | import org.bouncycastle.jcajce.provider.digest.SHA3;
6 | import java.beans.PropertyDescriptor;
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.IOException;
9 | import java.lang.reflect.Field;
10 | import java.lang.reflect.Method;
11 |
12 | public abstract class Entry {
13 |
14 | public abstract String typ();
15 |
16 | public abstract void writeForHash(ByteArrayOutputStream out);
17 |
18 | public void mustWriteForHash(ByteArrayOutputStream out, Object data) {
19 | try {
20 | if (data == null) {
21 | throw new WriteForHashException("The field needs to be written to hash is null");
22 | }
23 | if (data instanceof Byte) {
24 | OutputUtil.writeByte(out, (byte) data);
25 | } else if (data instanceof Long) {
26 | OutputUtil.writeLong(out, (long) data);
27 | } else if (data instanceof byte[]) {
28 | OutputUtil.writeVarstr(out, (byte[]) data);
29 | } else if (data instanceof byte[][]) {
30 | OutputUtil.writeVarstrList(out, (byte[][]) data);
31 | } else if (data instanceof String) {
32 | OutputUtil.writeVarstr(out, ((String) data).getBytes());
33 | } else if (data instanceof Hash) {
34 | out.write(((Hash) data).toByteArray());
35 | } else if (data instanceof AssetID) {
36 | out.write(((AssetID) data).toByteArray());
37 | } else if (data.getClass().isArray()) {
38 | Object[] array = (Object[]) data;
39 | OutputUtil.writeVarint(out, array.length);
40 | for (Object obj : array) {
41 | mustWriteForHash(out, obj);
42 | }
43 | } else {
44 | Class> cls = data.getClass();
45 | Field[] fields = cls.getDeclaredFields();
46 | for (Field field : fields) {
47 | PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), cls);
48 | Method readMethod = descriptor.getReadMethod();
49 | mustWriteForHash(out, readMethod.invoke(data));
50 | }
51 | }
52 | } catch (Exception e) {
53 | e.printStackTrace();
54 | throw new WriteForHashException(e);
55 | }
56 | }
57 |
58 | public Hash entryID() {
59 | SHA3.Digest256 digest256 = new SHA3.Digest256();
60 | ByteArrayOutputStream hasher = new ByteArrayOutputStream();
61 | ByteArrayOutputStream bh = new ByteArrayOutputStream();
62 | try {
63 | hasher.write("entryid:".getBytes());
64 | hasher.write(this.typ().getBytes());
65 | hasher.write(":".getBytes());
66 |
67 | this.writeForHash(bh);
68 | hasher.write(digest256.digest(bh.toByteArray()));
69 | return new Hash(digest256.digest(hasher.toByteArray()));
70 | } catch (IOException e) {
71 | e.printStackTrace();
72 | throw new RuntimeException(e);
73 | } finally {
74 | try {
75 | bh.close();
76 | hasher.close();
77 | } catch (IOException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/ExpandedKeys.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | public class ExpandedKeys {
4 |
5 | private String expandedPriKey;
6 |
7 | private String expandedPubKey;
8 |
9 | public void setPriKey(String priKey) {
10 | this.expandedPriKey = priKey;
11 | }
12 |
13 | public String getPriKey() {
14 | return this.expandedPriKey;
15 | }
16 |
17 | public void setPubKey(String pubKey) {
18 | this.expandedPubKey = pubKey;
19 | }
20 |
21 | public String getPubKey() {
22 | return this.expandedPubKey;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Hash.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 |
5 | import java.util.Objects;
6 |
7 | public class Hash {
8 |
9 | private String hexValue;
10 |
11 | public Hash() {}
12 |
13 | public Hash(String hexValue) {
14 | this.hexValue = hexValue;
15 | }
16 |
17 | public Hash(byte[] byteArray) {
18 | this.hexValue = Hex.toHexString(byteArray);
19 | }
20 |
21 | public byte[] toByteArray() {
22 | return Hex.decode(this.hexValue);
23 | }
24 |
25 | @Override
26 | public boolean equals(Object o) {
27 | if (this == o) return true;
28 | if (o == null || getClass() != o.getClass()) return false;
29 | Hash hash = (Hash) o;
30 | return Objects.equals(hexValue, hash.hexValue);
31 | }
32 |
33 | @Override
34 | public int hashCode() {
35 | return Objects.hash(hexValue);
36 | }
37 |
38 | @Override
39 | public String toString() {
40 | return this.hexValue;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/InputEntry.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.util.Map;
4 |
5 | public abstract class InputEntry extends Entry {
6 |
7 | protected int ordinal;
8 |
9 | public abstract void setDestination(Hash id, long pos, Map entryMap);
10 |
11 | public int getOrdinal() {
12 | return ordinal;
13 | }
14 |
15 | public void setOrdinal(int ordinal) {
16 | this.ordinal = ordinal;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Issue.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.util.Map;
5 |
6 | public class Issue extends InputEntry {
7 |
8 | private Hash nonceHash;
9 | private AssetAmount assetAmount;
10 | private AssetDefinition assetDefinition;
11 | private ValueDestination witnessDestination;
12 |
13 | public Issue(Hash nonceHash, AssetAmount assetAmount, int ordinal, AssetDefinition assetDefinition) {
14 | this.nonceHash = nonceHash;
15 | this.assetAmount = assetAmount;
16 | this.ordinal = ordinal;
17 | this.assetDefinition = assetDefinition;
18 | }
19 |
20 | @Override
21 | public void setDestination(Hash id, long pos, Map entryMap) {
22 | this.witnessDestination = new ValueDestination(id, this.assetAmount, pos);
23 | }
24 |
25 | @Override
26 | public String typ() {
27 | return "issuance1";
28 | }
29 |
30 | @Override
31 | public void writeForHash(ByteArrayOutputStream out) {
32 | mustWriteForHash(out, this.nonceHash);
33 | mustWriteForHash(out, this.assetAmount);
34 | }
35 |
36 | public Hash getNonceHash() {
37 | return nonceHash;
38 | }
39 |
40 | public void setNonceHash(Hash nonceHash) {
41 | this.nonceHash = nonceHash;
42 | }
43 |
44 | public AssetAmount getAssetAmount() {
45 | return assetAmount;
46 | }
47 |
48 | public void setAssetAmount(AssetAmount assetAmount) {
49 | this.assetAmount = assetAmount;
50 | }
51 |
52 | public AssetDefinition getAssetDefinition() {
53 | return assetDefinition;
54 | }
55 |
56 | public void setAssetDefinition(AssetDefinition assetDefinition) {
57 | this.assetDefinition = assetDefinition;
58 | }
59 |
60 | public ValueDestination getWitnessDestination() {
61 | return witnessDestination;
62 | }
63 |
64 | public void setWitnessDestination(ValueDestination witnessDestination) {
65 | this.witnessDestination = witnessDestination;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Mux.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | public class Mux extends Entry {
8 |
9 | private ValueSource[] sources;
10 |
11 | private Program program;
12 |
13 | private List witnessDestinations = new ArrayList<>();
14 |
15 | public Mux() {}
16 |
17 | public Mux(ValueSource[] sources, Program program) {
18 | this.sources = sources;
19 | this.program = program;
20 | }
21 |
22 | @Override
23 | public String typ() {
24 | return "mux1";
25 | }
26 |
27 | @Override
28 | public void writeForHash(ByteArrayOutputStream out) {
29 | mustWriteForHash(out, this.sources);
30 | mustWriteForHash(out, this.program);
31 | }
32 |
33 | public ValueSource[] getSources() {
34 | return sources;
35 | }
36 |
37 | public void setSources(ValueSource[] sources) {
38 | this.sources = sources;
39 | }
40 |
41 | public Program getProgram() {
42 | return program;
43 | }
44 |
45 | public void setProgram(Program program) {
46 | this.program = program;
47 | }
48 |
49 | public List getWitnessDestinations() {
50 | return witnessDestinations;
51 | }
52 |
53 | public void setWitnessDestinations(List witnessDestinations) {
54 | this.witnessDestinations = witnessDestinations;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/OutputEntry.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 |
5 | public class OutputEntry extends Entry {
6 |
7 | private ValueSource source;
8 |
9 | private Program controlProgram;
10 |
11 | private Integer ordinal;
12 |
13 | public OutputEntry() {
14 | this.source = new ValueSource();
15 | this.controlProgram = new Program();
16 | }
17 |
18 |
19 | public OutputEntry(ValueSource source, Program controlProgram, Integer ordinal) {
20 | this.source = source;
21 | this.controlProgram = controlProgram;
22 | this.ordinal = ordinal;
23 | }
24 |
25 | @Override
26 | public String typ() {
27 | return "output1";
28 | }
29 |
30 | @Override
31 | public void writeForHash(ByteArrayOutputStream out) {
32 | mustWriteForHash(out, this.source);
33 | mustWriteForHash(out, this.controlProgram);
34 | }
35 |
36 | public ValueSource getSource() {
37 | return source;
38 | }
39 |
40 | public void setSource(ValueSource source) {
41 | this.source = source;
42 | }
43 |
44 | public Program getControlProgram() {
45 | return controlProgram;
46 | }
47 |
48 | public void setControlProgram(Program controlProgram) {
49 | this.controlProgram = controlProgram;
50 | }
51 |
52 | public Integer getOrdinal() {
53 | return ordinal;
54 | }
55 |
56 | public void setOrdinal(Integer ordinal) {
57 | this.ordinal = ordinal;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Program.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 |
4 | public class Program {
5 |
6 | private long vmVersion;
7 |
8 | private byte[] code;
9 |
10 | public Program() {}
11 |
12 | public Program(long vmVersion, byte[] code) {
13 | this.vmVersion = vmVersion;
14 | this.code = code;
15 | }
16 |
17 | public long getVmVersion() {
18 | return vmVersion;
19 | }
20 |
21 | public void setVmVersion(long vmVersion) {
22 | this.vmVersion = vmVersion;
23 | }
24 |
25 | public byte[] getCode() {
26 | return code;
27 | }
28 |
29 | public void setCode(byte[] code) {
30 | this.code = code;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Retirement.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 |
5 | public class Retirement extends Entry {
6 |
7 | private ValueSource valueSource;
8 | private int ordinal;
9 |
10 | public Retirement(ValueSource valueSource, int ordinal) {
11 | this.valueSource = valueSource;
12 | this.ordinal = ordinal;
13 | }
14 |
15 |
16 | @Override
17 | public String typ() {
18 | return "retirement1";
19 | }
20 |
21 | @Override
22 | public void writeForHash(ByteArrayOutputStream out) {
23 | mustWriteForHash(out, valueSource);
24 | }
25 |
26 | public ValueSource getValueSource() {
27 | return valueSource;
28 | }
29 |
30 | public void setValueSource(ValueSource valueSource) {
31 | this.valueSource = valueSource;
32 | }
33 |
34 | public int getOrdinal() {
35 | return ordinal;
36 | }
37 |
38 | public void setOrdinal(int ordinal) {
39 | this.ordinal = ordinal;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/Spend.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.util.Map;
5 |
6 | public class Spend extends InputEntry {
7 |
8 | private Hash spentOutputID;
9 |
10 | private int ordinal;
11 |
12 | private ValueDestination witnessDestination;
13 |
14 | private byte[][] witnessArguments;
15 |
16 | public Spend(Hash spentOutputID, int ordinal) {
17 | this.spentOutputID = spentOutputID;
18 | this.ordinal = ordinal;
19 | }
20 |
21 | @Override
22 | public void setDestination(Hash id, long pos, Map entryMap) {
23 | OutputEntry spendOutput = (OutputEntry) entryMap.get(this.spentOutputID);
24 | this.witnessDestination = new ValueDestination(id, spendOutput.getSource().getValue(), pos);
25 | }
26 |
27 | @Override
28 | public String typ() {
29 | return "spend1";
30 | }
31 |
32 | @Override
33 | public void writeForHash(ByteArrayOutputStream out) {
34 | mustWriteForHash(out, this.spentOutputID);
35 | }
36 |
37 | public Hash getSpentOutputID() {
38 | return spentOutputID;
39 | }
40 |
41 | public void setSpentOutputID(Hash spentOutputID) {
42 | this.spentOutputID = spentOutputID;
43 | }
44 |
45 | @Override
46 | public int getOrdinal() {
47 | return ordinal;
48 | }
49 |
50 | @Override
51 | public void setOrdinal(int ordinal) {
52 | this.ordinal = ordinal;
53 | }
54 |
55 | public ValueDestination getWitnessDestination() {
56 | return witnessDestination;
57 | }
58 |
59 | public void setWitnessDestination(ValueDestination witnessDestination) {
60 | this.witnessDestination = witnessDestination;
61 | }
62 |
63 | public byte[][] getWitnessArguments() {
64 | return witnessArguments;
65 | }
66 |
67 | public void setWitnessArguments(byte[][] witnessArguments) {
68 | this.witnessArguments = witnessArguments;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/TxHeader.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | import java.io.ByteArrayOutputStream;
4 |
5 | public class TxHeader extends Entry {
6 |
7 | private long version;
8 |
9 | private long serializedSize;
10 |
11 | private long timeRange;
12 |
13 | private Hash[] resultIDs;
14 |
15 | public TxHeader() {}
16 |
17 | public TxHeader(long version, long serializedSize, long timeRange, Hash[] resultIDs) {
18 | this.version = version;
19 | this.serializedSize = serializedSize;
20 | this.timeRange = timeRange;
21 | this.resultIDs = resultIDs;
22 | }
23 |
24 | @Override
25 | public String typ() {
26 | return "txheader";
27 | }
28 |
29 | @Override
30 | public void writeForHash(ByteArrayOutputStream out) {
31 | mustWriteForHash(out, this.version);
32 | mustWriteForHash(out, this.timeRange);
33 | mustWriteForHash(out, this.resultIDs);
34 | }
35 |
36 | public long getVersion() {
37 | return version;
38 | }
39 |
40 | public void setVersion(long version) {
41 | this.version = version;
42 | }
43 |
44 | public long getSerializedSize() {
45 | return serializedSize;
46 | }
47 |
48 | public void setSerializedSize(long serializedSize) {
49 | this.serializedSize = serializedSize;
50 | }
51 |
52 | public long getTimeRange() {
53 | return timeRange;
54 | }
55 |
56 | public void setTimeRange(long timeRange) {
57 | this.timeRange = timeRange;
58 | }
59 |
60 | public Hash[] getResultIDs() {
61 | return resultIDs;
62 | }
63 |
64 | public void setResultIDs(Hash[] resultIDs) {
65 | this.resultIDs = resultIDs;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/ValueDestination.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 | public class ValueDestination {
4 |
5 | private Hash ref;
6 |
7 | private AssetAmount value;
8 |
9 | private long position;
10 |
11 | public ValueDestination() {}
12 |
13 | public ValueDestination(Hash ref, AssetAmount value, long position) {
14 | this.ref = ref;
15 | this.value = value;
16 | this.position = position;
17 | }
18 |
19 | public Hash getRef() {
20 | return ref;
21 | }
22 |
23 | public void setRef(Hash ref) {
24 | this.ref = ref;
25 | }
26 |
27 | public AssetAmount getValue() {
28 | return value;
29 | }
30 |
31 | public void setValue(AssetAmount value) {
32 | this.value = value;
33 | }
34 |
35 | public long getPosition() {
36 | return position;
37 | }
38 |
39 | public void setPosition(long position) {
40 | this.position = position;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/types/ValueSource.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.types;
2 |
3 |
4 | public class ValueSource {
5 |
6 | private Hash ref;
7 |
8 | private AssetAmount value;
9 |
10 | private long position;
11 |
12 | public ValueSource() {}
13 |
14 | public ValueSource(Hash ref, AssetAmount value, long position) {
15 | this.ref = ref;
16 | this.value = value;
17 | this.position = position;
18 | }
19 |
20 | public Hash getRef() {
21 | return ref;
22 | }
23 |
24 | public void setRef(Hash ref) {
25 | this.ref = ref;
26 | }
27 |
28 | public AssetAmount getValue() {
29 | return value;
30 | }
31 |
32 | public void setValue(AssetAmount value) {
33 | this.value = value;
34 | }
35 |
36 | public long getPosition() {
37 | return position;
38 | }
39 |
40 | public void setPosition(long position) {
41 | this.position = position;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/util/OutputUtil.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.util;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 |
6 | public final class OutputUtil {
7 |
8 | public static void writeByte(OutputStream out, byte data) throws IOException {
9 | out.write(data);
10 | }
11 |
12 | public static void writeLong(OutputStream out, long data) throws IOException {
13 | for (int i = 0; i < 8; i++) {
14 | out.write((byte) data);
15 | data >>= 8;
16 | }
17 | }
18 |
19 | public static void writeVarint(OutputStream out, long data) throws IOException {
20 | byte[] buf = new byte[9];
21 | int n = putUvarint(buf, data);
22 | out.write(buf, 0, n);
23 | }
24 |
25 | public static void writeVarstr(OutputStream out, byte[] str) throws IOException {
26 | writeVarint(out, str.length);
27 | out.write(str);
28 | }
29 |
30 | public static void writeVarstrList(OutputStream out, byte[][] l) throws IOException {
31 | writeVarint(out, l.length);
32 | for (byte[] str : l) {
33 | writeVarstr(out, str);
34 | }
35 | }
36 |
37 | private static int putUvarint(byte[] buf, long x) {
38 | int i = 0;
39 | while (x >= 0x80) {
40 | buf[i] = (byte) (x | 0x80);
41 | x >>= 7;
42 | i++;
43 | }
44 | buf[i] = (byte) x;
45 | return i + 1;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/util/PathUtil.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.util;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 |
8 | public class PathUtil {
9 | private static String intToPath(int index) {
10 | byte[] result = new byte[4];
11 | result[3] = (byte) (index >> 24 & 0xff);
12 | result[2] = (byte) (index >> 16 & 0xff);
13 | result[1] = (byte) (index >> 8 & 0xff);
14 | result[0] = (byte) (index >> 0 & 0xff);
15 | return Hex.toHexString(result);
16 | }
17 |
18 |
19 | // paths = {
20 | //// "2c000000",
21 | //// "99000000",
22 | //// "01000000", accountIndex
23 | //// "00000000", change
24 | //// "01000000" controlProgramIndex
25 | //// }
26 |
27 | public static byte[][] getBip44Path(int accountIndex, boolean change, int programIndex) {
28 |
29 | String accountIndexStr = intToPath(accountIndex);
30 | String changeStr = "00000000";
31 | String programIndexStr = intToPath(programIndex);
32 | if (change) {
33 | changeStr = "01000000";
34 | }
35 | return new byte[][]{
36 | Hex.decode("2c000000"),
37 | Hex.decode("99000000"),
38 | Hex.decode(accountIndexStr),
39 | Hex.decode(changeStr),
40 | Hex.decode(programIndexStr),
41 | };
42 | }
43 |
44 | public static byte[][] getBip32Path(byte keySpace, long accountIndex, long ...itemIndexes) {
45 | byte[] signerPath = new byte[9];
46 | signerPath[0] = keySpace;
47 | byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(accountIndex).array();
48 | System.arraycopy(path, 0, signerPath, 1, 8);
49 |
50 | byte[][] res = new byte[1 + itemIndexes.length][];
51 | res[0] = signerPath;
52 | for (int i = 0; i < itemIndexes.length; i++) {
53 | path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(itemIndexes[i]).array();
54 | res[i + 1] = path;
55 | }
56 | return res;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tx-signer/src/main/java/io/bytom/offline/util/SHA3Util.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.util;
2 |
3 | import org.bouncycastle.jcajce.provider.digest.SHA3;
4 |
5 | import java.io.ByteArrayOutputStream;
6 |
7 | public class SHA3Util {
8 | public static byte[] hashSha256(byte[] hash) {
9 | SHA3.Digest256 digest256 = new SHA3.Digest256();
10 | ByteArrayOutputStream out = new ByteArrayOutputStream();
11 | out.write(hash, 0, hash.length);
12 | byte[] data = out.toByteArray();
13 | return digest256.digest(data);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tx-signer/src/main/resources/config.properties:
--------------------------------------------------------------------------------
1 | bytom.api.url=
2 | client.access.token=
3 | # bytom.api.url=http://10.100.7.47:9888/
4 | # client.access.token=wt:3d17dbb953cedd53353bf3f342bb2929e9505105ffeb21670e6bd00abeef3772
5 | #bytom.api.url=http://127.0.0.1:9888/
6 | #client.access.token=sheng:49d1623f5991c62a5094e761477ddd2838dceb49c22fbf84b492a54f1df88123
7 |
--------------------------------------------------------------------------------
/tx-signer/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=debug, stdout, R
2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
4 | log4j.logger.org.apache.commons.httpclient=info
5 | log4j.logger.httpclient.wire.content=info
6 | log4j.logger.httpclient.wire.header=info
7 | # Pattern to output the caller's file name and line number.
8 | log4j.appender.stdout.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n
9 | log4j.appender.R=org.apache.log4j.RollingFileAppender
10 | log4j.appender.R.File=bytom.log
11 | log4j.appender.R.MaxFileSize=1000KB
12 | # Keep one backup file
13 | log4j.appender.R.MaxBackupIndex=1
14 | log4j.appender.R.layout=org.apache.log4j.PatternLayout
15 | log4j.appender.R.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/api/SignTransactionTest.java:
--------------------------------------------------------------------------------
1 |
2 | package io.bytom.offline.api;
3 |
4 | import io.bytom.offline.types.*;
5 | import org.bouncycastle.util.encoders.Hex;
6 | import org.junit.Test;
7 |
8 | import java.io.ByteArrayOutputStream;
9 |
10 | /**
11 | * Created by liqiang on 2018/10/24.
12 | */
13 | public class SignTransactionTest {
14 |
15 | //以下为测试用的区块上的交易utxo,即output中第二个输出
16 | //新交易接收地址为bm1qdpc5sejdkm22uv23jwd8pg6lyqz2trz4trgxh0,需要找零
17 | /*{
18 | "id": "3b36453f7dc03b13523d6431afd7e544f60339daed52ba8fca7ebf88cd5e5939",
19 | "version": 1,
20 | "size": 330,
21 | "time_range": 0,
22 | "inputs": [
23 | {
24 | "type": "spend",
25 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
26 | "asset_definition": {},
27 | "amount": 482000000,
28 | "control_program": "00148da6ccbc216f9019cf80d23fd2083c80e29fcba2",
29 | "address": "bm1q3knve0ppd7gpnnuq6glayzpusr3fljazzcq0eh",
30 | "spent_output_id": "d11967ce15741217c650bc0b9dd7a390aaedd8ea5c645266920a7d19d8be681a",
31 | "input_id": "caae7c37f6cecce6854e6488cc389379e312acd2f7495337633501fc7f72b5f3"
32 | }
33 | ],
34 | "outputs": [
35 | {
36 | "type": "control",
37 | "id": "3110bc8e7d713c17fb3dc3c9deadbfc419a25c25252c8e613d1fa54cc4d05dbd",
38 | "position": 0,
39 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
40 | "asset_definition": {},
41 | "amount": 281500000,
42 | "control_program": "00145d6ba5bf0cfdb2487abd594429cd04c2ba566f9f",
43 | "address": "bm1qt446t0cvlkeys74at9zznngyc2a9vmulcr2xy6"
44 | },
45 | {
46 | "type": "control",
47 | "id": "db5afebb5b33aec2c46fcebb20b98fffa8c065a101f4c1789fe5491b34dc1b8f",
48 | "position": 1,
49 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
50 | "asset_definition": {},
51 | "amount": 200000000,
52 | "control_program": "00140d074bc86bd388a45f1c8911a41b8f0705d9058b",
53 | "address": "bm1qp5r5hjrt6wy2ghcu3yg6gxu0quzajpvtsm2gnc"
54 | }
55 | ],
56 | "status_fail": false,
57 | "mux_id": "0e97230a7347967764fd77c8cfa96b38ec6ff08465300a01900c645dfb694f24"
58 | }*/
59 |
60 |
61 | @Test
62 | public void testMustWriteForHash() throws Exception {
63 | Entry entry = new Entry() {
64 | @Override
65 | public String typ() {
66 | return null;
67 | }
68 |
69 | @Override
70 | public void writeForHash(ByteArrayOutputStream out) {
71 |
72 | }
73 | };
74 |
75 | ByteArrayOutputStream out = new ByteArrayOutputStream();
76 | entry.mustWriteForHash(out, (byte) 2);
77 | assert Hex.toHexString(out.toByteArray()).equals("02");
78 |
79 | out.reset();
80 | entry.mustWriteForHash(out, 1L);
81 | assert Hex.toHexString(out.toByteArray()).equals("0100000000000000");
82 |
83 | out.reset();
84 | entry.mustWriteForHash(out, 0x3456584738473837L);
85 | assert Hex.toHexString(out.toByteArray()).equals("3738473847585634");
86 |
87 | out.reset();
88 | entry.mustWriteForHash(out, new byte[]{0x12, 0x34, (byte) 0x85});
89 | assert Hex.toHexString(out.toByteArray()).equals("03123485");
90 |
91 | out.reset();
92 | entry.mustWriteForHash(out, new byte[][]{{0x12, 0x34, (byte) 0x85}, {(byte) 0x86, 0x17, 0x40}});
93 | assert Hex.toHexString(out.toByteArray()).equals("020312348503861740");
94 |
95 | out.reset();
96 | entry.mustWriteForHash(out, "hello, 世界");
97 | assert Hex.toHexString(out.toByteArray()).equals("0d68656c6c6f2c20e4b896e7958c");
98 |
99 | out.reset();
100 | entry.mustWriteForHash(out, new String[]{"hi", "你好", "hello"});
101 | assert Hex.toHexString(out.toByteArray()).equals("0302686906e4bda0e5a5bd0568656c6c6f");
102 |
103 | out.reset();
104 | String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f";
105 | entry.mustWriteForHash(out, new Hash(hash));
106 | assert Hex.toHexString(out.toByteArray()).equals(hash);
107 |
108 | out.reset();
109 | ValueSource valueSource = new ValueSource(new Hash(hash), null, 1);
110 | Program program = new Program(1, new byte[]{1});
111 | Mux mux = new Mux(new ValueSource[]{valueSource}, program);
112 | entry.mustWriteForHash(out, mux);
113 | assert Hex.toHexString(out.toByteArray()).equals("01d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f010000000000000001000000000000000101");
114 | }
115 |
116 | @Test
117 | public void testEntryID() throws Exception {
118 | String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f";
119 | ValueSource valueSource = new ValueSource(new Hash(hash), null, 1);
120 | Program program = new Program(1, new byte[]{1});
121 | Mux mux = new Mux(new ValueSource[]{valueSource}, program);
122 | String entryID = mux.entryID().toString();
123 | assert entryID.equals("ebd967df33a3373ab85521fba24c22bf993c73f46fa96254b0c86646093184e9");
124 | }
125 |
126 | }
127 |
128 |
129 |
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/api/SignerTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.api;
2 |
3 | import io.bytom.offline.common.Signer;
4 | import org.bouncycastle.util.encoders.Hex;
5 | import org.junit.Test;
6 |
7 | import java.security.InvalidKeyException;
8 | import java.security.NoSuchAlgorithmException;
9 | import java.security.SignatureException;
10 |
11 | public class SignerTest {
12 |
13 | @Test
14 | public void testEd25519InnerSign() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
15 | String rootXprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";
16 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";
17 | String expandedXprv = "20849f14bbe212d1b8917da2e1eda9afc4c21e9dd0a47f1e169a3326d45ae443236f54b987369e86ed78eb2b0a2def89a69ec69ca1059e2efe045796dc583d91";
18 | String hashedMessage = "99ab9ebdba106466371467b036d56a0e54ad2a6035e365a6103ba97ab553fd52";
19 | byte[] sig = Signer.ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage));
20 | System.out.println("sig:" + Hex.toHexString(sig));
21 | //expected: e628e980c690d9ef4ca8a2edee1654a6b401edc4f1af7bda3ffd97fe412522c3bab671dd4e51d0aeeb64f8d761fbdb03e296ab0c1dcbed4eafa504f412a98100
22 | }
23 | }
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/common/DerivePrivateKeyTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 | import org.junit.Test;
5 |
6 | import java.security.InvalidKeyException;
7 | import java.security.NoSuchAlgorithmException;
8 | import java.security.SignatureException;
9 |
10 | public class DerivePrivateKeyTest {
11 | @Test
12 | public void testBip44Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
13 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";
14 | byte[] derivePrivateKey = DerivePrivateKey.bip44derivePrvKey(rootKey, 1, false, 2);
15 | System.out.println(Hex.toHexString(derivePrivateKey));
16 | //expected 48c65f40d860723e71b03988a22edc9ad00ae0deae992e79fb3b812edb5c3e43e78065bf46d0e8ad922cdae600fd2c2a6239b8f1f504f8f255460c6fcce023ff
17 | }
18 |
19 | @Test
20 | public void testBip32Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
21 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";
22 | byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 2, (byte) 0);
23 | System.out.println(Hex.toHexString(derivePrivateKey));
24 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc
25 | }
26 |
27 | @Test
28 | public void testBip32PublicKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
29 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";
30 | byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 14, (byte) 0);
31 | byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey);
32 | System.out.println(Hex.toHexString(deriveXpub).substring(0, 64));
33 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/common/DeriveXpubTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 |
4 | import org.bouncycastle.util.encoders.Hex;
5 | import org.junit.Test;
6 |
7 | public class DeriveXpubTest {
8 |
9 | @Test
10 | public void testDeriveXpub() {
11 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";
12 | byte[] xpub = DeriveXpub.deriveXpub(Hex.decode(hxprv));
13 | System.out.println("hxpub: " + Hex.toHexString(xpub));
14 | //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b
15 | // d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b
16 | }
17 | }
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/common/ExpandedPrivateKeyTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 |
4 | import org.bouncycastle.util.encoders.Hex;
5 | import org.junit.Test;
6 |
7 | import java.security.InvalidKeyException;
8 | import java.security.NoSuchAlgorithmException;
9 | import java.security.SignatureException;
10 |
11 | public class ExpandedPrivateKeyTest {
12 |
13 | @Test
14 | public void testExpandedKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
15 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";
16 | byte[] z = ExpandedPrivateKey.expandedPrivateKey(Hex.decode(childXprv));
17 | System.out.println(Hex.toHexString(z));
18 | //expect: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3
19 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3
20 | }
21 | }
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/common/NonHardenedChildTest.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.common;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 | import org.junit.Test;
5 |
6 | import java.security.InvalidKeyException;
7 | import java.security.NoSuchAlgorithmException;
8 | import java.security.SignatureException;
9 |
10 |
11 | public class NonHardenedChildTest {
12 |
13 | @Test
14 | public void testNHChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
15 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";
16 | byte[] xprv = Hex.decode(hxprv);
17 | //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b
18 | String[] hpaths = {"010400000000000000", "0100000000000000"};
19 | byte[][] paths = new byte[][]{
20 | Hex.decode(hpaths[0]),
21 | Hex.decode(hpaths[1])
22 | };
23 | byte[] res = xprv;
24 | for (int i = 0; i < hpaths.length; i++) {
25 | byte[] xpub = DeriveXpub.deriveXpub(res);
26 | // System.out.println("xpub: "+Hex.toHexString(xpub));
27 | res = NonHardenedChild.nhChild(paths[i], res, xpub);
28 | }
29 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954
30 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954
31 | }
32 |
33 | @Test
34 | public void testChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
35 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";
36 | String[] hpaths = {"010400000000000000", "0100000000000000"};
37 | byte[] childXprv = NonHardenedChild.child(Hex.decode(hxprv), hpaths);
38 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954
39 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954
40 | }
41 | }
--------------------------------------------------------------------------------
/tx-signer/src/test/java/io/bytom/offline/util/SHA3256Test.java:
--------------------------------------------------------------------------------
1 | package io.bytom.offline.util;
2 |
3 | import org.bouncycastle.util.encoders.Hex;
4 | import org.junit.Test;
5 |
6 | public class SHA3256Test {
7 | @Test
8 | public void testSHA3Hash() {
9 | String rawTransaction = "7b0a202022646563696d616c73223a20382c0a202022646573637" +
10 | "2697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d";
11 | byte[] bytes = SHA3Util.hashSha256(Hex.decode(rawTransaction));
12 | System.out.println(Hex.toHexString(bytes));
13 | //expected 69ab19b3907f40e4f264dbd3f71967654e0e93f836026918af8861932ed0409b
14 | }
15 |
16 |
17 | }
18 |
--------------------------------------------------------------------------------