OSDN Git Service

refactor
[bytom/bytom-java-sdk.git] / tx-signer / README_zh.md
1 # tx-signer
2 严格来说,tx-signer并不属于SDK,它是bytomd中构建交易、对交易签名两大模块的java实现版。因此,若想用tx-signer对交易进行离线签名,需要由你在本地保管好自己的私钥。
3
4 如果你的目的是完全脱离于bytomd全节点,可能需要自己做更多额外的工作。比如,在构建交易时,需要花费若干个utxo(Unspent Transaction Output)作为交易的输入,如果没有全节点则需要自身来维护utxo。当使用tx-signer构建完成一笔交易并签名后,若没有全节点的帮助,也需要自己实现P2P网络协议将交易广播到其他节点。
5
6 本文不会对以上技术细节进行讨论,而是利用bytomd全节点查询可用的utxo构建交易,对交易进行签名并序列化后,同样使用bytomd提交交易。
7
8 ## 准备工作
9
10 #### 将Maven依赖引入到你的项目中
11 1. 获取SDK源码  
12   
13    ```
14    git clone https://github.com/Bytom/bytom-java-sdk.git  
15    ```  
16   
17 2. 打包成JAR包并安装到本地的Maven仓库
18   
19    ```  
20    $ mvn clean install -DskipTests  
21    ```  
22   
23 3. 在项目的POM文件中添加依赖。其中,第一个依赖是bytomd api的封装,可用于查询可用的utxo以及提交交易;第二个依赖用于构建交易以及对交易进行离线签名。
24     ```xml
25     <dependency>
26         <groupId>io.bytom</groupId>
27         <artifactId>java-sdk</artifactId>
28         <version>1.0.0</version>
29     </dependency>
30  
31     <dependency>
32         <groupId>io.bytom</groupId>
33         <artifactId>tx-signer</artifactId>
34         <version>1.0.0</version>
35     </dependency>
36     ```
37     
38 ## 构建交易
39
40 #### 普通交易
41 **1.** 查询可用的utxo
42
43 在本文中,以下将全部使用全节点来查询可用的utxo,你也可以构建一套自己的utxo维护方案。
44 ```java
45 Client client = Client.generateClient();
46 UnspentOutput.QueryBuilder builder = new UnspentOutput.QueryBuilder();
47 builder.accountAlias = "bytom";
48 List<UnspentOutput> outputs = builder.list(client);
49 ```
50 利用SDK只需要四行代码就能查询可用的utxo(SDK具体文档详见[java-sdk documentation](java-sdk/README.md))。在QueryBuilder中可以指定是否为未确认的utxo(默认false),也可以通过from和count来进行分页查询(默认查询所有)。
51 假设在当前账户下查询得到这样一个utxo:
52 ```json
53 {
54     "account_alias": "bytom",
55     "id": "ffdc59d0478277298de4afa458dfa7623c051a46b7a84939fb8227083411b156",
56     "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
57     "asset_alias": "BTM",
58     "amount": 41250000000,
59     "account_id": "0G1R52O1G0A02",
60     "address": "sm1qxls6ajp6fejc0j5kp8jwt2nj3kmsqazfumrkrr",
61     "control_program_index": 1,
62     "program": "001437e1aec83a4e6587ca9609e4e5aa728db7007449",
63     "source_id": "2d3a5d920833778cc7c65d7c96fe5f3c4a1a61aa086ee093f44a0522dd499a34",
64     "source_pos": 0,
65     "valid_height": 4767,
66     "change": false,
67     "derive_rule": 0
68 }
69
70 ```
71
72 **2.** 构建交易
73
74 现在需要往`0014c832e1579b4f96dc12dcfff39e8fe69a62d3f516`这个control program转100个BTM。代码如下:
75
76 ```java
77 String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
78 // 下面的字段与utxo中的字段一一对应
79 SpendInput input = new SpendInput();
80 input.setAssetId(btmAssetID);
81 input.setAmount(41250000000L);
82 input.setProgram("001437e1aec83a4e6587ca9609e4e5aa728db7007449");
83 input.setSourcePosition(0);
84 input.setSourceID("2d3a5d920833778cc7c65d7c96fe5f3c4a1a61aa086ee093f44a0522dd499a34");
85 input.setChange(false);
86 input.setControlProgramIndex(1);
87 // 选择使用BIP32还是BIP44来派生地址,默认BIP44
88 input.setBipProtocol(BIPProtocol.BIP44);
89 // 账户对应的密钥索引
90 input.setKeyIndex(1);
91 // 自身本地保管的私钥,用于对交易进行签名
92 input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
93
94 Transaction tx = new Transaction.Builder()
95                 .addInput(input)
96                 // 加入需要转入的output
97                 .addOutput(new Output(btmAssetID, 10000000000L, "0014c832e1579b4f96dc12dcfff39e8fe69a62d3f516"))
98                 // 剩余的BTM用于找零
99                 .addOutput(new Output(btmAssetID, 31250000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf"))
100                 .setTimeRange(0)
101                 .build();
102
103 String rawTransaction = tx.rawTransaction();
104
105 ```
106
107 对交易调用build方法后,自动会对交易进行本地的验证和签名操作。注意,在本地只是做简单的字段验证,本地验证通过并不代表交易合法。最后对交易调用rawTransaction方法返回交易序列化后的字符串。
108
109
110 **3. 提交交易**
111
112 本文利用bytomd全节点来提交交易:
113 ```java
114 HashMap<String, Object> body = new HashMap<>();
115 body.put("raw_transaction", "070100010160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302401cb779288be890a28c5209036da1a27d9fe74a51c38e0a10db4817bcf4fd05f68580239eea7dcabf19f144c77bf13d3674b5139aa51a99ba58118386c190af0e20bcbe020b05e1b7d0825953d92bf47897be08cd7751a37adb95d6a2e5224f55ab02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80b095e42001160014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c000149ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d293ad03012200200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac6600");
116 Transaction.SubmitResponse response = client.request("submit-transaction", body, Transaction.SubmitResponse.class);
117 ```
118 交易提交成功后,response返回交易ID。
119
120
121 #### 发行资产交易
122 **1. 查询可用的utxo**
123
124 发行资产时,需要使用BTM作为手续费,因此第一步同样需要查询当前账户下可用的utxo,由于上面已经提到,这里不再赘述。
125
126 **2. 查询需要发行的资产信息**
127
128 例如,需要发行的资产id为`7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14`
129
130 ```java
131 Asset.QueryBuilder builder = new Asset.QueryBuilder();
132 builder.setId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");
133 List<Asset> assets = builder.list(client);
134 ```
135
136 假设查询得到的资产信息如下:
137 ```json
138 {
139             "type": "asset",
140             "xpubs": [
141                 "5ff7f79f0fd4eb9ccb17191b0a1ac9bed5b4a03320a06d2ff8170dd51f9ad9089c4038ec7280b5eb6745ef3d36284e67f5cf2ed2a0177d462d24abf53c0399ed"
142             ],
143             "quorum": 1,
144             "key_index": 3,
145             "derive_rule": 0,
146             "id": "7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14",
147             "alias": "棒棒鸡",
148             "vm_version": 1,
149             "issue_program": "ae20db11f9dfa39c9e66421c530fe027218edd3d5b1cd98f24c826f4d9c0cd131a475151ad",
150             "raw_definition_byte": "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d",
151             "definition": {
152                 "decimals": 8,
153                 "description": {},
154                 "name": "",
155                 "symbol": ""
156             }
157 }
158 ```
159
160 **3. 构建交易**
161
162 现在需要发行1000个棒棒鸡资产:
163
164 ```java
165 IssuanceInput issuanceInput = new IssuanceInput();
166 issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");
167 issuanceInput.setAmount(100000000000L);
168 // issue program
169 issuanceInput.setProgram("ae20db11f9dfa39c9e66421c530fe027218edd3d5b1cd98f24c826f4d9c0cd131a475151ad");
170 // 可以不指定,不指定时将随机生成一个
171 issuanceInput.setNonce("ac9d5a527f5ab00a");
172 issuanceInput.setKeyIndex(5);
173 // raw definition byte
174 issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d");
175 // 该资产对应的私钥
176 issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
177
178 // 创建一个spend input作为手续费,假设当前有一个100BTM的utxo,并且使用1BTM作为手续费,则后续还要创建99BTM的找零地址
179 SpendInput feeInput = new SpendInput(btmAssetID, 10000000000L, "0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");
180 feeInput.setKeyIndex(1);
181 feeInput.setChange(true);
182 feeInput.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");
183 feeInput.setSourcePosition(2);
184 feeInput.setControlProgramIndex(457);
185 feeInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");
186
187 Transaction tx = new Transaction.Builder()
188                 .addInput(issuanceInput)
189                 .addInput(feeInput)
190                 // 该output用于接收发行的资产
191                 .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449"))
192                 // 找零
193                 .addOutput(new Output(btmAssetID, 9800000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56"))
194                 .setTimeRange(0)
195                 .build();
196 ```
197
198 **4. 提交交易**
199
200 提交交易的方式与普通交易一致。
201
202
203 #### 销毁资产交易
204
205 销毁资产跟发行资产类似,同样需要BTM作为手续费。
206
207 **1. 查询可用的utxo**
208
209 查询方式与普通交易一致。
210
211 **2. 构建交易**
212
213 这里以销毁一个BTM为例,假设查询得到一个100BTM的utxo:
214
215 ```java
216 // 查询得到一个100BTM的utxo作为输入
217 SpendInput input = new SpendInput(btmAssetID, 10000000000L, "0014f1dc52048f439ac7fd74f8106a21da78f00de48f");
218 input.setRootPrivateKey(rootKey);
219 input.setChange(true);
220 input.setKeyIndex(1);
221 input.setControlProgramIndex(41);
222 input.setSourceID("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65");
223 input.setSourcePosition(0);
224
225 // 销毁资产时,可添加一段附加的文本
226 String arbitrary = "77656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c";
227 // 销毁99个BTM,剩余1个BTM作为手续费
228 Output output = Output.newRetireOutput(btmAssetID, 9900000000L, arbitrary);
229
230 Transaction transaction = new Transaction.Builder()
231                 .addInput(input)
232                 .addOutput(output)
233                 .setTimeRange(2000000)
234                 .build();
235
236 String rawTransaction = transaction.rawTransaction();
237
238 ```
239
240 **4. 提交交易**
241
242 提交交易的方式与普通交易一致。