OSDN Git Service

refactor
[bytom/bytom-java-sdk.git] / tx-signer / src / main / java / io / bytom / api / Transaction.java
index bd81646..48e9bd5 100755 (executable)
@@ -1,14 +1,11 @@
 package io.bytom.api;\r
 \r
-import com.google.gson.annotations.SerializedName;\r
-import io.bytom.common.ParameterizedTypeImpl;\r
-import io.bytom.common.SuccessRespon;\r
 import io.bytom.common.Utils;\r
-import io.bytom.exception.BytomException;\r
-import io.bytom.http.Client;\r
-import org.apache.log4j.Logger;\r
+import io.bytom.types.*;\r
+import org.bouncycastle.util.encoders.Hex;\r
 \r
-import java.lang.reflect.Type;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
 import java.util.ArrayList;\r
 import java.util.HashMap;\r
 import java.util.List;\r
@@ -20,7 +17,6 @@ import java.util.Map;
 \r
 public class Transaction {\r
 \r
-    @SerializedName("tx_id")\r
     public String txID;\r
     /**\r
      * version\r
@@ -33,321 +29,170 @@ public class Transaction {
     /**\r
      * time_range\r
      */\r
-    @SerializedName("time_range")\r
     public Integer timeRange;\r
 \r
     /**\r
-     * status\r
-     */\r
-    public Integer fee;\r
-\r
-    /**\r
      * List of specified inputs for a transaction.\r
      */\r
-    public List<AnnotatedInput> inputs;\r
+    public List<BaseInput> inputs;\r
 \r
     /**\r
      * List of specified outputs for a transaction.\r
      */\r
-    public List<AnnotatedOutput> outputs;\r
-\r
-    //    public InputWitnessComponent inputWitnessComponent;\r
-    private static Logger logger = Logger.getLogger(Transaction.class);\r
-\r
-    public String toJson() {\r
-        return Utils.serializer.toJson(this);\r
+    public List<Output> outputs;\r
+\r
+    public Transaction(Builder builder) {\r
+        this.inputs = builder.inputs;\r
+        this.outputs = builder.outputs;\r
+        this.version = builder.version;\r
+        this.size = builder.size;\r
+        this.timeRange = builder.timeRange;\r
+        mapTx();\r
+        sign();\r
     }\r
 \r
-    public static Transaction fromJson(String json) {\r
-        return Utils.serializer.fromJson(json, Transaction.class);\r
-    }\r
-\r
-    public static Transaction fromSuccessRespon(String json) {\r
-        Type responType = new ParameterizedTypeImpl(SuccessRespon.class, new Class[]{Transaction.class});\r
-        SuccessRespon<Transaction> result = Utils.serializer.fromJson(json, responType);\r
-        return result.dataObject;\r
-    }\r
+    public static class Builder {\r
 \r
-    public static Transaction decode(Client client, String txId) throws BytomException {\r
-        Map<String, Object> req = new HashMap<String, Object>();\r
-        req.put("raw_transaction", txId);\r
-        Transaction Transaction =\r
-                client.request("decode-raw-transaction", req, Transaction.class);\r
+        private String txID;\r
 \r
-        logger.info("decode-raw-transaction:");\r
-        logger.info(Transaction.toJson());\r
+        private Integer version = 1;\r
 \r
-        return Transaction;\r
-    }\r
+        private Integer size = 0;\r
 \r
-    public static class Builder {\r
-        @SerializedName("tx_id")\r
-        public String txID;\r
-        public Integer version;\r
-        public Integer size;\r
-        @SerializedName("time_range")\r
-        public Integer timeRange;\r
+        private Integer timeRange;\r
 \r
-        Transaction tx;\r
-        List<AnnotatedInput> inputs;\r
-        List<AnnotatedOutput> outputs;\r
+        private List<BaseInput> inputs;\r
+        private List<Output> outputs;\r
 \r
         public Builder() {\r
             this.inputs = new ArrayList<>();\r
             this.outputs = new ArrayList<>();\r
         }\r
 \r
-        public Builder addInput(AnnotatedInput input) {\r
+        public Builder addInput(BaseInput input) {\r
             this.inputs.add(input);\r
             return this;\r
         }\r
 \r
-        public Builder addOutput(AnnotatedOutput output) {\r
+        public Builder addOutput(Output output) {\r
             this.outputs.add(output);\r
             return this;\r
         }\r
 \r
-\r
-        public Transaction build(Integer version, Integer timeRange, Integer size) {\r
-            tx = new Transaction();\r
-            tx.inputs = this.inputs;\r
-            tx.outputs = this.outputs;\r
-            tx.version = version;\r
-            tx.timeRange = timeRange;\r
-            tx.size = size;\r
-            return tx;\r
-        }\r
-\r
-        public Transaction build(int timeRange) {\r
-            tx = new Transaction();\r
-            tx.inputs = this.inputs;\r
-            tx.outputs = this.outputs;\r
-            tx.version = 1;\r
-            tx.size = 0;\r
-            tx.timeRange = timeRange;\r
-            return tx;\r
-        }\r
-    }\r
-\r
-    public static class AnnotatedInput {\r
-\r
-        @SerializedName("input_id")\r
-        public String inputID;\r
-        /**\r
-         * address\r
-         */\r
-        public String address;\r
-\r
-        /**\r
-         * The number of units of the asset being issued or spent.\r
-         */\r
-        public long amount;\r
-\r
-        //        /**\r
-//         * The definition of the asset being issued or spent (possibly null).\r
-//         */\r
-//        @SerializedName("asset_definition")\r
-//        private Map<String, Object> assetDefinition;\r
-        @SerializedName("asset_definition")\r
-        public String assetDefinition;\r
-\r
-        /**\r
-         * The id of the asset being issued or spent.\r
-         */\r
-        @SerializedName("asset_id")\r
-        public String assetId;\r
-\r
-        /**\r
-         * The control program which must be satisfied to transfer this output.\r
-         */\r
-        @SerializedName("control_program")\r
-        public String controlProgram;\r
-\r
-        /**\r
-         * The id of the output consumed by this input. Null if the input is an\r
-         * issuance.\r
-         */\r
-        @SerializedName("spent_output_id")\r
-        public String spentOutputId;\r
-\r
-        /**\r
-         * The type of the input.<br>\r
-         * Possible values are "issue" and "spend".\r
-         */\r
-        public int type;\r
-\r
-        public String sourceId;\r
-\r
-        public long sourcePosition;\r
-\r
-        public String nonce;\r
-\r
-        private int controlProgramIndex;\r
-        private boolean change;\r
-\r
-        public int keyIndex;\r
-        public String chainPath;\r
-\r
-        @SerializedName("witness_component")\r
-        public InputWitnessComponent witnessComponent;\r
-\r
-        @Override\r
-        public String toString() {\r
-            return Utils.serializer.toJson(this);\r
-        }\r
-\r
-        public int getControlProgramIndex() {\r
-            return controlProgramIndex;\r
-        }\r
-\r
-        public AnnotatedInput setControlProgramIndex(int controlProgramIndex) {\r
-            this.controlProgramIndex = controlProgramIndex;\r
+        public Builder setTimeRange(int timeRange) {\r
+            this.timeRange = timeRange;\r
             return this;\r
         }\r
 \r
-        public boolean isChange() {\r
-            return change;\r
-        }\r
-\r
-        public AnnotatedInput setChange(boolean change) {\r
-            this.change = change;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setAmount(long amount) {\r
-            this.amount = amount;\r
-            return this;\r
+        public Transaction build() {\r
+            return new Transaction(this);\r
         }\r
-\r
-        public AnnotatedInput setAssetId(String assetId) {\r
-            this.assetId = assetId;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setControlProgram(String controlProgram) {\r
-            this.controlProgram = controlProgram;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setType(int type) {\r
-            this.type = type;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setSourceId(String sourceId) {\r
-            this.sourceId = sourceId;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setSourcePosition(long sourcePosition) {\r
-            this.sourcePosition = sourcePosition;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setNonce(String nonce) {\r
-            this.nonce = nonce;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedInput setAssetDefinition(String assetDefinition) {\r
-            this.assetDefinition = assetDefinition;\r
-            return this;\r
-        }\r
-\r
-        public int getKeyIndex() {\r
-            return keyIndex;\r
-        }\r
-\r
-        public AnnotatedInput setKeyIndex(int keyIndex) {\r
-            this.keyIndex = keyIndex;\r
-            return this;\r
-        }\r
-\r
     }\r
 \r
-    public static class AnnotatedOutput {\r
-\r
-        /**\r
-         * address\r
-         */\r
-        public String address;\r
-\r
-        /**\r
-         * The number of units of the asset being controlled.\r
-         */\r
-        public long amount;\r
-\r
-        /**\r
-         * The definition of the asset being controlled (possibly null).\r
-         */\r
-        @SerializedName("asset_definition")\r
-        public Map<String, Object> assetDefinition;\r
-\r
-        /**\r
-         * The id of the asset being controlled.\r
-         */\r
-        @SerializedName("asset_id")\r
-        public String assetId;\r
-\r
-        /**\r
-         * The control program which must be satisfied to transfer this output.\r
-         */\r
-        @SerializedName("control_program")\r
-        public String controlProgram;\r
-\r
-        /**\r
-         * The id of the output.\r
-         */\r
-        @SerializedName("id")\r
-        public String id;\r
-\r
-        /**\r
-         * The output's position in a transaction's list of outputs.\r
-         */\r
-        public Integer position;\r
-\r
-        /**\r
-         * The type the output.<br>\r
-         * Possible values are "control" and "retire".\r
-         */\r
-        public String type;\r
-\r
-        public AnnotatedOutput setAddress(String address) {\r
-            this.address = address;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedOutput setAmount(long amount) {\r
-            this.amount = amount;\r
-            return this;\r
-        }\r
-\r
-        public AnnotatedOutput setAssetId(String assetId) {\r
-            this.assetId = assetId;\r
-            return this;\r
+    private void sign() {\r
+        for (BaseInput input : inputs) {\r
+            try {\r
+                input.buildWitness(txID);\r
+            } catch (Exception e) {\r
+                e.printStackTrace();\r
+                throw new RuntimeException(e);\r
+            }\r
         }\r
+    }\r
 \r
-        public AnnotatedOutput setControlProgram(String controlProgram) {\r
-            this.controlProgram = controlProgram;\r
-            return this;\r
-        }\r
+    public String rawTransaction() {\r
+        String rawTransaction;\r
+        //开始序列化\r
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();\r
+        try {\r
+            stream.write(7);\r
+            // version\r
+            if (null != version)\r
+                Utils.writeVarint(version, stream);\r
+            if (null != timeRange)\r
+                Utils.writeVarint(timeRange, stream);\r
+            //inputs\r
+            if (null != inputs && inputs.size() > 0) {\r
+                Utils.writeVarint(inputs.size(), stream);\r
+                for (BaseInput input : inputs) {\r
+                    System.out.println(Hex.toHexString(input.serializeInput()));\r
+                    stream.write(input.serializeInput());\r
+                }\r
+            }\r
+\r
+            //outputs\r
+            if (null != outputs && outputs.size() > 0) {\r
+                Utils.writeVarint(outputs.size(), stream);\r
+                for (Output output : outputs) {\r
+                    stream.write(output.serializeOutput());\r
+                }\r
+            }\r
+            byte[] data = stream.toByteArray();\r
+            rawTransaction = Hex.toHexString(data);\r
+        } catch (IOException e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        return rawTransaction;\r
+    }\r
 \r
-        public AnnotatedOutput setPosition(Integer position) {\r
-            this.position = position;\r
-            return this;\r
+    private void mapTx() {\r
+        Map<Hash, Entry> entryMap = new HashMap<>();\r
+        ValueSource[] muxSources = new ValueSource[inputs.size()];\r
+        List<InputEntry> inputEntries = new ArrayList<>();\r
+\r
+        try {\r
+            for (int i = 0; i < inputs.size(); i++) {\r
+                BaseInput input = inputs.get(i);\r
+                InputEntry inputEntry =  input.convertInputEntry(entryMap, i);\r
+                Hash spendID = addEntry(entryMap, inputEntry);\r
+                input.setInputID(spendID.toString());\r
+\r
+                muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0);\r
+                inputEntries.add(inputEntry);\r
+            }\r
+\r
+            Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));\r
+            Hash muxID = addEntry(entryMap, mux);\r
+            for (InputEntry inputEntry : inputEntries) {\r
+                inputEntry.setDestination(muxID, inputEntry.ordinal, entryMap);\r
+            }\r
+\r
+            List<Hash> resultIDList = new ArrayList<>();\r
+            for (int i = 0; i < outputs.size(); i++) {\r
+                Output output = outputs.get(i);\r
+\r
+                AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount);\r
+                ValueSource src = new ValueSource(muxID, amount, i);\r
+\r
+                Hash resultID;\r
+                if (output.controlProgram.startsWith("6a")) {\r
+                    Retirement retirement = new Retirement(src, i);\r
+                    resultID = addEntry(entryMap, retirement);\r
+                } else {\r
+                    Program prog = new Program(1, Hex.decode(output.controlProgram));\r
+                    OutputEntry oup = new OutputEntry(src, prog, i);\r
+                    resultID = addEntry(entryMap, oup);\r
+                }\r
+\r
+                resultIDList.add(resultID);\r
+                output.id = resultID.toString();\r
+\r
+                ValueDestination destination = new ValueDestination(resultID, src.value, 0);\r
+                mux.witnessDestinations.add(destination);\r
+            }\r
+\r
+            TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{}));\r
+            Hash txID = addEntry(entryMap, txHeader);\r
+            this.txID = txID.toString();\r
+\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
         }\r
     }\r
 \r
-    /**\r
-     * A single witness component, holding information that will become the input\r
-     * witness.\r
-     */\r
-    public static class InputWitnessComponent {\r
-\r
-        /**\r
-         * The list of signatures made with the specified keys (null unless type is\r
-         * "signature").\r
-         */\r
-        public String[] signatures;\r
+    private Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {\r
+        Hash id = entry.entryID();\r
+        entryMap.put(id, entry);\r
+        return id;\r
     }\r
 }\r