OSDN Git Service

perfect exception process
[bytom/bytom-java-sdk.git] / tx-signer / src / main / java / io / bytom / api / Transaction.java
1 package io.bytom.api;\r
2 \r
3 import io.bytom.common.Utils;\r
4 import io.bytom.exception.MapTransactionException;\r
5 import io.bytom.exception.SerializeTransactionException;\r
6 import io.bytom.exception.SignTransactionException;\r
7 import io.bytom.types.*;\r
8 import org.bouncycastle.util.encoders.Hex;\r
9 import java.io.ByteArrayOutputStream;\r
10 import java.io.IOException;\r
11 import java.util.ArrayList;\r
12 import java.util.HashMap;\r
13 import java.util.List;\r
14 import java.util.Map;\r
15 \r
16 /**\r
17  * Created by liqiang on 2018/10/24.\r
18  */\r
19 \r
20 public class Transaction {\r
21 \r
22     private String txID;\r
23     /**\r
24      * version\r
25      */\r
26     private Integer version;\r
27     /**\r
28      * size\r
29      */\r
30     private Integer size;\r
31     /**\r
32      * time_range\r
33      */\r
34     private Integer timeRange;\r
35 \r
36     /**\r
37      * List of specified inputs for a transaction.\r
38      */\r
39     private List<BaseInput> inputs;\r
40 \r
41     /**\r
42      * List of specified outputs for a transaction.\r
43      */\r
44     private List<Output> outputs;\r
45 \r
46     public Transaction(Builder builder) {\r
47         this.inputs = builder.inputs;\r
48         this.outputs = builder.outputs;\r
49         this.version = builder.version;\r
50         this.size = builder.size;\r
51         this.timeRange = builder.timeRange;\r
52 \r
53         this.validate();\r
54         this.mapTransaction();\r
55         this.sign();\r
56     }\r
57 \r
58     public static class Builder {\r
59 \r
60         private Integer version = 1;\r
61 \r
62         private Integer size = 0;\r
63 \r
64         private Integer timeRange;\r
65 \r
66         private List<BaseInput> inputs;\r
67         private List<Output> outputs;\r
68 \r
69         public Builder() {\r
70             this.inputs = new ArrayList<>();\r
71             this.outputs = new ArrayList<>();\r
72         }\r
73 \r
74         public Builder addInput(BaseInput input) {\r
75             this.inputs.add(input);\r
76             return this;\r
77         }\r
78 \r
79         public Builder addOutput(Output output) {\r
80             this.outputs.add(output);\r
81             return this;\r
82         }\r
83 \r
84         public Builder setTimeRange(int timeRange) {\r
85             this.timeRange = timeRange;\r
86             return this;\r
87         }\r
88 \r
89         public Transaction build() {\r
90             return new Transaction(this);\r
91         }\r
92     }\r
93 \r
94     private void sign() {\r
95         for (BaseInput input : inputs) {\r
96             try {\r
97                 input.buildWitness(txID);\r
98             } catch (Exception e) {\r
99                 e.printStackTrace();\r
100                 throw new SignTransactionException(e);\r
101             }\r
102         }\r
103     }\r
104 \r
105     public String rawTransaction() {\r
106         ByteArrayOutputStream stream = new ByteArrayOutputStream();\r
107         try {\r
108             stream.write(7);\r
109 \r
110             Utils.writeVarint(version, stream);\r
111 \r
112             Utils.writeVarint(timeRange, stream);\r
113 \r
114             Utils.writeVarint(inputs.size(), stream);\r
115             for (BaseInput input : inputs) {\r
116                 stream.write(input.serializeInput());\r
117             }\r
118 \r
119             Utils.writeVarint(outputs.size(), stream);\r
120             for (Output output : outputs) {\r
121                 stream.write(output.serializeOutput());\r
122             }\r
123         } catch (IOException e) {\r
124             e.printStackTrace();\r
125             throw new SerializeTransactionException(e);\r
126         }\r
127         return Hex.toHexString(stream.toByteArray());\r
128     }\r
129 \r
130     private void validate() {\r
131         if (version == null) {\r
132             throw new IllegalArgumentException("the version of transaction must be specified.");\r
133         }\r
134         if (timeRange == null) {\r
135             throw new IllegalArgumentException("the time range of transaction must be specified.");\r
136         }\r
137         if (size == null) {\r
138             throw new IllegalArgumentException("the size range of transaction must be specified.");\r
139         }\r
140 \r
141         inputs.forEach(BaseInput::validate);\r
142     }\r
143 \r
144     private void mapTransaction() {\r
145         Map<Hash, Entry> entryMap = new HashMap<>();\r
146         ValueSource[] muxSources = new ValueSource[inputs.size()];\r
147         List<InputEntry> inputEntries = new ArrayList<>();\r
148 \r
149         try {\r
150             for (int i = 0; i < inputs.size(); i++) {\r
151                 BaseInput input = inputs.get(i);\r
152                 InputEntry inputEntry =  input.convertInputEntry(entryMap, i);\r
153                 Hash spendID = addEntry(entryMap, inputEntry);\r
154                 input.setInputID(spendID.toString());\r
155 \r
156                 muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0);\r
157                 inputEntries.add(inputEntry);\r
158             }\r
159 \r
160             Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));\r
161             Hash muxID = addEntry(entryMap, mux);\r
162             for (InputEntry inputEntry : inputEntries) {\r
163                 inputEntry.setDestination(muxID, inputEntry.ordinal, entryMap);\r
164             }\r
165 \r
166             List<Hash> resultIDList = new ArrayList<>();\r
167             for (int i = 0; i < outputs.size(); i++) {\r
168                 Output output = outputs.get(i);\r
169 \r
170                 AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount);\r
171                 ValueSource src = new ValueSource(muxID, amount, i);\r
172 \r
173                 Hash resultID;\r
174                 if (output.controlProgram.startsWith("6a")) {\r
175                     Retirement retirement = new Retirement(src, i);\r
176                     resultID = addEntry(entryMap, retirement);\r
177                 } else {\r
178                     Program prog = new Program(1, Hex.decode(output.controlProgram));\r
179                     OutputEntry oup = new OutputEntry(src, prog, i);\r
180                     resultID = addEntry(entryMap, oup);\r
181                 }\r
182 \r
183                 resultIDList.add(resultID);\r
184                 output.id = resultID.toString();\r
185 \r
186                 ValueDestination destination = new ValueDestination(resultID, src.value, 0);\r
187                 mux.witnessDestinations.add(destination);\r
188             }\r
189 \r
190             TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{}));\r
191             Hash txID = addEntry(entryMap, txHeader);\r
192             this.txID = txID.toString();\r
193 \r
194         } catch (Exception e) {\r
195             throw new MapTransactionException(e);\r
196         }\r
197     }\r
198 \r
199     private Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {\r
200         Hash id = entry.entryID();\r
201         entryMap.put(id, entry);\r
202         return id;\r
203     }\r
204 \r
205     public String getTxID() {\r
206         return txID;\r
207     }\r
208 \r
209     public Integer getVersion() {\r
210         return version;\r
211     }\r
212 \r
213     public Integer getSize() {\r
214         return size;\r
215     }\r
216 \r
217     public Integer getTimeRange() {\r
218         return timeRange;\r
219     }\r
220 \r
221     public List<BaseInput> getInputs() {\r
222         return inputs;\r
223     }\r
224 \r
225     public List<Output> getOutputs() {\r
226         return outputs;\r
227     }\r
228 }\r