1 package io.bytom.api;
\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
17 * Created by liqiang on 2018/10/24.
\r
20 public class Transaction {
\r
22 private String txID;
\r
26 private Integer version;
\r
30 private Integer size;
\r
34 private Integer timeRange;
\r
37 * List of specified inputs for a transaction.
\r
39 private List<BaseInput> inputs;
\r
42 * List of specified outputs for a transaction.
\r
44 private List<Output> outputs;
\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
58 public static class Builder {
\r
60 private Integer version = 1;
\r
62 private Integer size = 0;
\r
64 private Integer timeRange;
\r
66 private List<BaseInput> inputs;
\r
67 private List<Output> outputs;
\r
70 this.inputs = new ArrayList<>();
\r
71 this.outputs = new ArrayList<>();
\r
74 public Builder addInput(BaseInput input) {
\r
75 this.inputs.add(input);
\r
79 public Builder addOutput(Output output) {
\r
80 this.outputs.add(output);
\r
84 public Builder setTimeRange(int timeRange) {
\r
85 this.timeRange = timeRange;
\r
89 public Transaction build() {
\r
90 return new Transaction(this);
\r
94 private void sign() {
\r
95 for (BaseInput input : inputs) {
\r
97 input.buildWitness(txID);
\r
98 } catch (Exception e) {
\r
99 e.printStackTrace();
\r
100 throw new SignTransactionException(e);
\r
105 public String rawTransaction() {
\r
106 ByteArrayOutputStream stream = new ByteArrayOutputStream();
\r
110 Utils.writeVarint(version, stream);
\r
112 Utils.writeVarint(timeRange, stream);
\r
114 Utils.writeVarint(inputs.size(), stream);
\r
115 for (BaseInput input : inputs) {
\r
116 stream.write(input.serializeInput());
\r
119 Utils.writeVarint(outputs.size(), stream);
\r
120 for (Output output : outputs) {
\r
121 stream.write(output.serializeOutput());
\r
123 } catch (IOException e) {
\r
124 e.printStackTrace();
\r
125 throw new SerializeTransactionException(e);
\r
127 return Hex.toHexString(stream.toByteArray());
\r
130 private void validate() {
\r
131 if (version == null) {
\r
132 throw new IllegalArgumentException("the version of transaction must be specified.");
\r
134 if (timeRange == null) {
\r
135 throw new IllegalArgumentException("the time range of transaction must be specified.");
\r
137 if (size == null) {
\r
138 throw new IllegalArgumentException("the size range of transaction must be specified.");
\r
141 for (BaseInput input : inputs) {
\r
146 private void mapTx() {
\r
147 Map<Hash, Entry> entryMap = new HashMap<>();
\r
148 ValueSource[] muxSources = new ValueSource[inputs.size()];
\r
149 List<InputEntry> inputEntries = new ArrayList<>();
\r
152 for (int i = 0; i < inputs.size(); i++) {
\r
153 BaseInput input = inputs.get(i);
\r
154 InputEntry inputEntry = input.convertInputEntry(entryMap, i);
\r
155 Hash spendID = addEntry(entryMap, inputEntry);
\r
156 input.setInputID(spendID.toString());
\r
158 muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0);
\r
159 inputEntries.add(inputEntry);
\r
162 Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));
\r
163 Hash muxID = addEntry(entryMap, mux);
\r
164 for (InputEntry inputEntry : inputEntries) {
\r
165 inputEntry.setDestination(muxID, inputEntry.getOrdinal(), entryMap);
\r
168 List<Hash> resultIDList = new ArrayList<>();
\r
169 for (int i = 0; i < outputs.size(); i++) {
\r
170 Output output = outputs.get(i);
\r
172 AssetAmount amount = new AssetAmount(new AssetID(output.getAssetId()), output.getAmount());
\r
173 ValueSource src = new ValueSource(muxID, amount, i);
\r
176 if (output.getControlProgram().startsWith("6a")) {
\r
177 Retirement retirement = new Retirement(src, i);
\r
178 resultID = addEntry(entryMap, retirement);
\r
180 Program program = new Program(1, Hex.decode(output.getControlProgram()));
\r
181 OutputEntry oup = new OutputEntry(src, program, i);
\r
182 resultID = addEntry(entryMap, oup);
\r
185 resultIDList.add(resultID);
\r
186 output.setId(resultID.toString());
\r
188 ValueDestination destination = new ValueDestination(resultID, src.getValue(), 0);
\r
189 mux.getWitnessDestinations().add(destination);
\r
192 TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{}));
\r
193 Hash txID = addEntry(entryMap, txHeader);
\r
194 this.txID = txID.toString();
\r
196 } catch (Exception e) {
\r
197 throw new MapTransactionException(e);
\r
201 private Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {
\r
202 Hash id = entry.entryID();
\r
203 entryMap.put(id, entry);
\r
207 public String getTxID() {
\r
211 public Integer getVersion() {
\r
215 public Integer getSize() {
\r
219 public Integer getTimeRange() {
\r
223 public List<BaseInput> getInputs() {
\r
227 public List<Output> getOutputs() {
\r