OSDN Git Service

offine signer
authordoraemon <wyjDoraemon@163.com>
Tue, 13 Jul 2021 09:42:14 +0000 (17:42 +0800)
committerdoraemon <wyjDoraemon@163.com>
Tue, 13 Jul 2021 09:42:14 +0000 (17:42 +0800)
20 files changed:
tx-signer/src/main/java/io/bytom/offline/api/BaseInput.java
tx-signer/src/main/java/io/bytom/offline/api/BaseOutput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/api/CoinbaseInput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/api/IssuanceInput.java
tx-signer/src/main/java/io/bytom/offline/api/OriginalOutput.java [moved from tx-signer/src/main/java/io/bytom/offline/api/Output.java with 73% similarity]
tx-signer/src/main/java/io/bytom/offline/api/SpendInput.java
tx-signer/src/main/java/io/bytom/offline/api/StateData.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/api/Transaction.java
tx-signer/src/main/java/io/bytom/offline/api/VetoInput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/types/Coinbase.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/types/Output.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/offline/types/OutputEntry.java
tx-signer/src/main/java/io/bytom/offline/types/ValueSource.java
tx-signer/src/main/java/io/bytom/offline/types/Veto.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/types/Vote.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/offline/util/AssetIdUtil.java [new file with mode: 0644]
tx-signer/src/test/java/io/bytom/offline/api/AppTest.java [deleted file]
tx-signer/src/test/java/io/bytom/offline/api/TransactionTest.java [new file with mode: 0644]
tx-signer/src/test/java/io/bytom/offline/api/TxInputTest.java [new file with mode: 0644]
tx-signer/src/test/java/io/bytom/offline/api/TxOutputTest.java [new file with mode: 0644]

index 6b909a7..bce8182 100644 (file)
@@ -11,6 +11,8 @@ public abstract class BaseInput {
 
     static final int ISSUANCE_INPUT_TYPE = 0;
     static final int SPEND_INPUT_TYPE = 1;
+    static final int Coinbase_INPUT_TYPE = 2;
+    static final int Veto_INPUT_TYPE = 3;
 
     static final byte AssetKeySpace = 0;
     static final byte AccountKeySpace = 1;
@@ -52,7 +54,7 @@ public abstract class BaseInput {
 
     public byte[] serializeInput() throws IOException {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        // assert version
+        // asset version
         Utils.writeVarint(1, stream);
         Utils.writeExtensibleString(serializeInputCommitment(), stream);
         Utils.writeExtensibleString(serializeInputWitness(), stream);
@@ -133,4 +135,8 @@ public abstract class BaseInput {
     public void setKeyIndex(int keyIndex) {
         this.keyIndex = keyIndex;
     }
+
+    public void appendWitnessComponent(String witness){
+        witnessComponent.appendWitness(witness);
+    }
 }
diff --git a/tx-signer/src/main/java/io/bytom/offline/api/BaseOutput.java b/tx-signer/src/main/java/io/bytom/offline/api/BaseOutput.java
new file mode 100644 (file)
index 0000000..e1d9f96
--- /dev/null
@@ -0,0 +1,55 @@
+package io.bytom.offline.api;
+
+import io.bytom.offline.common.Utils;
+import io.bytom.offline.types.*;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public abstract class BaseOutput {
+    /**
+     * The number of units of the asset being controlled.
+     */
+    private Long amount;
+
+    /**
+     * The id of the asset being controlled.
+     */
+    private String assetId;
+
+    /**
+     * The control program which must be satisfied to transfer this output.
+     */
+    private String controlProgram;
+
+    /**
+     * The id of the output.
+     */
+    private String id;
+
+    StateData stateData = new StateData();
+
+    public BaseOutput() {
+    }
+
+    public BaseOutput(String assetID, long amount, String controlProgram) {
+        this.assetId = assetID;
+        this.amount = amount;
+        this.controlProgram = controlProgram;
+    }
+
+
+    public abstract byte[] serializeOutputCommitment() throws IOException;
+
+
+    public byte[] serializeOutput() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        // asset version
+        Utils.writeVarint(1, stream);
+        Utils.writeExtensibleString(serializeOutputCommitment(), stream);
+        return stream.toByteArray();
+    }
+
+}
\ No newline at end of file
diff --git a/tx-signer/src/main/java/io/bytom/offline/api/CoinbaseInput.java b/tx-signer/src/main/java/io/bytom/offline/api/CoinbaseInput.java
new file mode 100644 (file)
index 0000000..27045a4
--- /dev/null
@@ -0,0 +1,48 @@
+package io.bytom.offline.api;
+
+import io.bytom.offline.common.Utils;
+import io.bytom.offline.types.*;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class CoinbaseInput extends BaseInput{
+    private String arbitrary;
+
+    @Override
+    public InputEntry toInputEntry(Map<Hash, Entry> entryMap, int index) {
+        Coinbase coinbase =  new Coinbase(arbitrary);
+
+        Hash prevOutID = coinbase.entryID();
+        entryMap.put(prevOutID, coinbase);
+        return new Coinbase(prevOutID,index);
+    }
+
+    @Override
+    public void buildWitness(String txID) throws Exception {
+
+    }
+
+    @Override
+    public byte[] serializeInputCommitment() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        Utils.writeVarint(Coinbase_INPUT_TYPE, stream);
+        Utils.writeVarStr(Hex.decode(this.arbitrary),stream);
+        return stream.toByteArray();
+    }
+
+    @Override
+    public byte[] serializeInputWitness() throws IOException {
+        return new byte[0];
+    }
+
+    public String getArbitrary() {
+        return arbitrary;
+    }
+
+    public void setArbitrary(String arbitrary) {
+        this.arbitrary = arbitrary;
+    }
+}
index c3b4d16..b13f07b 100644 (file)
@@ -5,6 +5,7 @@ import io.bytom.offline.common.ExpandedPrivateKey;
 import io.bytom.offline.common.Signer;
 import io.bytom.offline.common.Utils;
 import io.bytom.offline.types.*;
+import io.bytom.offline.util.AssetIdUtil;
 import io.bytom.offline.util.SHA3Util;
 import org.bouncycastle.util.encoders.Hex;
 import java.io.ByteArrayOutputStream;
@@ -18,7 +19,15 @@ public class IssuanceInput extends BaseInput {
 
     private String rawAssetDefinition;
 
-    public IssuanceInput() {}
+    public IssuanceInput(){
+
+    }
+
+    public IssuanceInput(String rawAssetDefinition,String issuanceProgram) {
+        this.rawAssetDefinition = rawAssetDefinition;
+        this.setProgram(issuanceProgram);
+        this.setAssetId(AssetIdUtil.computeAssetID(rawAssetDefinition,issuanceProgram));
+    }
 
     public IssuanceInput(String assetID, Long amount, String issuanceProgram) {
         this.setAssetId(assetID);
@@ -66,7 +75,10 @@ public class IssuanceInput extends BaseInput {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         Utils.writeVarint(ISSUANCE_INPUT_TYPE, stream);
         Utils.writeVarStr(Hex.decode(nonce), stream);
-        stream.write(Hex.decode(getAssetId()));
+        if (this.getAssetId()==null){
+            this.setAssetId(AssetIdUtil.computeAssetID(rawAssetDefinition,this.getProgram()));
+        }
+        stream.write(Hex.decode(this.getAssetId()));
         Utils.writeVarint(getAmount(), stream);
         return stream.toByteArray();
     }
@@ -84,13 +96,16 @@ public class IssuanceInput extends BaseInput {
 
     @Override
     public void validate() {
-        super.validate();
         if (nonce == null) {
             throw new IllegalArgumentException("the nonce of issuance input must be specified.");
         }
         if (rawAssetDefinition == null) {
             throw new IllegalArgumentException("the nonce of issuance input must be specified.");
         }
+
+        if (this.getProgram() == null) {
+            throw new IllegalArgumentException("the program of issuance must be specified.");
+        }
     }
 
     public String getNonce() {
@@ -2,12 +2,12 @@ package io.bytom.offline.api;
 
 import io.bytom.offline.common.Utils;
 import io.bytom.offline.common.VMUtil;
+import io.bytom.offline.util.AssetIdUtil;
 import org.bouncycastle.util.encoders.Hex;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
-public class Output {
-
+public class OriginalOutput {
     /**
      * The number of units of the asset being controlled.
      */
@@ -28,27 +28,28 @@ public class Output {
      */
     private String id;
 
-    /**
-     * The output's position in a transaction's list of outputs.
-     */
-    private Integer position;
+    StateData stateData = new StateData();
+
+    public OriginalOutput() {
+    }
 
-    public Output(String assetID, long amount, String controlProgram) {
+    public OriginalOutput(String assetID, long amount, String controlProgram) {
         this.assetId = assetID;
         this.amount = amount;
         this.controlProgram = controlProgram;
     }
 
-    public static Output newRetireOutput(String assetID, long amount, String arbitrary) {
-        String retireProgram = Hex.toHexString(new byte[]{VMUtil.OP_FAIL}) + Hex.toHexString(VMUtil.pushDataBytes(Hex.decode(arbitrary)));
-        return new Output(assetID, amount, retireProgram);
+    public OriginalOutput(String rawAssetDefinition,String program) {
+        this.assetId = AssetIdUtil.computeAssetID(rawAssetDefinition,program);
+        this.controlProgram = program;
     }
 
     public byte[] serializeOutput() throws IOException {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
-
-        //assetVersion
-        Utils.writeVarint(1, stream); //AssetVersion是否默认为1
+        // asset version
+        Utils.writeVarint(1, stream);
+        //outputType
+        Utils.writeVarint(0, stream); //outputType
         //outputCommit
         ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();
         //assetId
@@ -59,6 +60,8 @@ public class Output {
         Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version
         //controlProgram
         Utils.writeVarStr(Hex.decode(controlProgram), outputCommitSteam);
+        //stateData
+        Utils.writeVarList(this.stateData.toByteArray(), outputCommitSteam);
 
         Utils.writeExtensibleString(outputCommitSteam.toByteArray(), stream);
 
@@ -67,6 +70,7 @@ public class Output {
         return stream.toByteArray();
     }
 
+
     public Long getAmount() {
         return amount;
     }
@@ -99,11 +103,7 @@ public class Output {
         this.id = id;
     }
 
-    public Integer getPosition() {
-        return position;
-    }
-
-    public void setPosition(Integer position) {
-        this.position = position;
+    public void appendStateData(String stateDataStr){
+        stateData.appendStateData(stateDataStr);
     }
 }
index 0a11c32..1803cde 100644 (file)
@@ -19,6 +19,8 @@ public class SpendInput extends BaseInput {
 
     private BIPProtocol bipProtocol = BIPProtocol.BIP44;
 
+    StateData stateData = new StateData();
+
     public SpendInput() {}
 
     public SpendInput(String assetID, long amount, String controlProgram) {
@@ -40,7 +42,7 @@ public class SpendInput extends BaseInput {
         Hash sourceID = new Hash(this.sourceID);
         ValueSource src = new ValueSource(sourceID, assetAmount, this.sourcePosition);
 
-        OutputEntry prevOut = new OutputEntry(src, pro, 0);
+        Output prevOut = new Output(src, pro, 0,stateData.toByteArray());
         Hash prevOutID = prevOut.entryID();
         entryMap.put(prevOutID, prevOut);
         return new Spend(prevOutID, index);
@@ -82,6 +84,7 @@ public class SpendInput extends BaseInput {
         // vm version
         Utils.writeVarint(1, spendCommitSteam);
         Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam);
+        Utils.writeVarList(stateData.toByteArray(), spendCommitSteam);
         Utils.writeExtensibleString(spendCommitSteam.toByteArray(), stream);
         return stream.toByteArray();
     }
@@ -129,4 +132,8 @@ public class SpendInput extends BaseInput {
     public void setBipProtocol(BIPProtocol bipProtocol) {
         this.bipProtocol = bipProtocol;
     }
+
+    public void appendStateData(String stateDataStr){
+        stateData.appendStateData(stateDataStr);
+    }
 }
\ No newline at end of file
diff --git a/tx-signer/src/main/java/io/bytom/offline/api/StateData.java b/tx-signer/src/main/java/io/bytom/offline/api/StateData.java
new file mode 100644 (file)
index 0000000..692dbd9
--- /dev/null
@@ -0,0 +1,26 @@
+package io.bytom.offline.api;
+
+import org.bouncycastle.util.encoders.Hex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StateData {
+    private List<String> stateData;
+
+    public StateData() {
+        stateData = new ArrayList<>();
+    }
+
+    public byte[][] toByteArray() {
+        byte[][] byteArray = new byte[stateData.size()][];
+        for (int i = 0; i < stateData.size(); i++) {
+            byteArray[i] = Hex.decode(stateData.get(i));
+        }
+        return byteArray;
+    }
+
+    public void appendStateData(String stateDataStr) {
+        stateData.add(stateDataStr);
+    }
+}
index 8798307..9165848 100755 (executable)
@@ -41,7 +41,7 @@ public class Transaction {
     /**\r
      * List of specified outputs for a transaction.\r
      */\r
-    private List<Output> outputs;\r
+    private List<OriginalOutput> outputs;\r
 \r
     public Transaction(Builder builder) {\r
         this.inputs = builder.inputs;\r
@@ -64,7 +64,7 @@ public class Transaction {
         private Integer timeRange;\r
 \r
         private List<BaseInput> inputs;\r
-        private List<Output> outputs;\r
+        private List<OriginalOutput> outputs;\r
 \r
         public Builder() {\r
             this.inputs = new ArrayList<>();\r
@@ -76,7 +76,7 @@ public class Transaction {
             return this;\r
         }\r
 \r
-        public Builder addOutput(Output output) {\r
+        public Builder addOutput(OriginalOutput output) {\r
             this.outputs.add(output);\r
             return this;\r
         }\r
@@ -86,12 +86,17 @@ public class Transaction {
             return this;\r
         }\r
 \r
+        public Builder setSize(int size) {\r
+            this.size = size;\r
+            return this;\r
+        }\r
+\r
         public Transaction build() {\r
             return new Transaction(this);\r
         }\r
     }\r
 \r
-    private void sign() {\r
+    public void sign() {\r
         for (BaseInput input : inputs) {\r
             try {\r
                 input.buildWitness(txID);\r
@@ -117,7 +122,7 @@ public class Transaction {
             }\r
 \r
             Utils.writeVarint(outputs.size(), stream);\r
-            for (Output output : outputs) {\r
+            for (OriginalOutput output : outputs) {\r
                 stream.write(output.serializeOutput());\r
             }\r
         } catch (IOException e) {\r
@@ -167,7 +172,7 @@ public class Transaction {
 \r
             List<Hash> resultIDList = new ArrayList<>();\r
             for (int i = 0; i < outputs.size(); i++) {\r
-                Output output = outputs.get(i);\r
+                OriginalOutput output = outputs.get(i);\r
 \r
                 AssetAmount amount = new AssetAmount(new AssetID(output.getAssetId()), output.getAmount());\r
                 ValueSource src = new ValueSource(muxID, amount, i);\r
@@ -178,7 +183,7 @@ public class Transaction {
                     resultID = addEntry(entryMap, retirement);\r
                 } else {\r
                     Program program = new Program(1, Hex.decode(output.getControlProgram()));\r
-                    OutputEntry oup = new OutputEntry(src, program, i);\r
+                    Output oup = new Output(src, program, i,output.stateData.toByteArray());\r
                     resultID = addEntry(entryMap, oup);\r
                 }\r
 \r
@@ -225,7 +230,7 @@ public class Transaction {
         return inputs;\r
     }\r
 \r
-    public List<Output> getOutputs() {\r
+    public List<OriginalOutput> getOutputs() {\r
         return outputs;\r
     }\r
 }\r
diff --git a/tx-signer/src/main/java/io/bytom/offline/api/VetoInput.java b/tx-signer/src/main/java/io/bytom/offline/api/VetoInput.java
new file mode 100644 (file)
index 0000000..4119726
--- /dev/null
@@ -0,0 +1,108 @@
+package io.bytom.offline.api;
+
+import io.bytom.offline.common.Utils;
+import io.bytom.offline.types.*;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class VetoInput extends BaseInput{
+    private String sourceID;
+
+    private Integer sourcePosition;
+
+    private Boolean change;
+
+    private Integer controlProgramIndex;
+
+    private byte[] vote;
+
+    StateData stateData = new StateData();
+
+    @Override
+    public InputEntry toInputEntry(Map<Hash, Entry> entryMap, int index) {
+        Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram()));
+        AssetAmount assetAmount = this.getAssetAmount();
+        Hash sourceID = new Hash(this.sourceID);
+        ValueSource src = new ValueSource(sourceID, assetAmount, this.sourcePosition);
+
+        Vote  prevOut = new Vote(src, pro, 0,this.vote,stateData.toByteArray());
+        Hash prevOutID = prevOut.entryID();
+        entryMap.put(prevOutID, prevOut);
+        return new Veto(prevOutID, index);
+    }
+
+    @Override
+    public void buildWitness(String txID) throws Exception {
+
+    }
+
+    @Override
+    public byte[] serializeInputCommitment() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        Utils.writeVarint(Veto_INPUT_TYPE, stream);
+
+        ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();
+        spendCommitSteam.write(Hex.decode(sourceID));
+        spendCommitSteam.write(Hex.decode(getAssetId()));
+        Utils.writeVarint(getAmount(), spendCommitSteam);
+        Utils.writeVarint(sourcePosition, spendCommitSteam);
+        // vm version
+        Utils.writeVarint(1, spendCommitSteam);
+        Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam);
+        Utils.writeVarList(stateData.toByteArray(), spendCommitSteam);
+        Utils.writeExtensibleString(spendCommitSteam.toByteArray(), stream);
+        Utils.writeVarStr(this.vote,stream);
+        return stream.toByteArray();
+    }
+
+    @Override
+    public byte[] serializeInputWitness() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        Utils.writeVarList(witnessComponent.toByteArray(), stream);
+        return stream.toByteArray();
+    }
+
+    public String getSourceID() {
+        return sourceID;
+    }
+
+    public void setSourceID(String sourceID) {
+        this.sourceID = sourceID;
+    }
+
+    public Integer getSourcePosition() {
+        return sourcePosition;
+    }
+
+    public void setSourcePosition(Integer sourcePosition) {
+        this.sourcePosition = sourcePosition;
+    }
+
+    public Boolean getChange() {
+        return change;
+    }
+
+    public void setChange(Boolean change) {
+        this.change = change;
+    }
+
+    public Integer getControlProgramIndex() {
+        return controlProgramIndex;
+    }
+
+    public void setControlProgramIndex(Integer controlProgramIndex) {
+        this.controlProgramIndex = controlProgramIndex;
+    }
+
+    public byte[] getVote() {
+        return vote;
+    }
+
+    public void setVote(byte[] vote) {
+        this.vote = vote;
+    }
+
+}
diff --git a/tx-signer/src/main/java/io/bytom/offline/types/Coinbase.java b/tx-signer/src/main/java/io/bytom/offline/types/Coinbase.java
new file mode 100644 (file)
index 0000000..94592e9
--- /dev/null
@@ -0,0 +1,50 @@
+package io.bytom.offline.types;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Map;
+
+public class Coinbase extends InputEntry{
+    private Hash coinbaseOutputID;
+
+    private int ordinal;
+
+    private ValueDestination witnessDestination;
+
+    private String arbitrary;
+
+    public Coinbase(Hash coinbaseOutputID, int ordinal) {
+        this.coinbaseOutputID = coinbaseOutputID;
+        this.ordinal = ordinal;
+    }
+
+    public Coinbase(String arbitrary) {
+        this.arbitrary = arbitrary;
+    }
+
+
+    @Override
+    public String typ() {
+        return "coinbase1";
+    }
+
+    @Override
+    public void writeForHash(ByteArrayOutputStream out) {
+        System.out.println("write for hash");
+        System.out.println(this.arbitrary);
+          mustWriteForHash(out,this.arbitrary);
+    }
+
+    @Override
+    public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) {
+//        OutputEntry spendOutput = (OutputEntry) entryMap.get(this.coinbaseOutputID);
+//        this.witnessDestination = new ValueDestination(id, spendOutput.getSource().getValue(), pos);
+    }
+
+    public String getArbitrary() {
+        return arbitrary;
+    }
+
+    public void setArbitrary(String arbitrary) {
+        this.arbitrary = arbitrary;
+    }
+}
diff --git a/tx-signer/src/main/java/io/bytom/offline/types/Output.java b/tx-signer/src/main/java/io/bytom/offline/types/Output.java
new file mode 100755 (executable)
index 0000000..a081c97
--- /dev/null
@@ -0,0 +1,30 @@
+package io.bytom.offline.types;
+
+import java.io.ByteArrayOutputStream;
+
+public class Output extends OutputEntry {
+    public Output() {
+        this.source = new ValueSource();
+        this.controlProgram = new Program();
+    }
+
+
+    public Output(ValueSource source, Program controlProgram, Integer ordinal, byte[][] stateData) {
+        this.source = source;
+        this.controlProgram = controlProgram;
+        this.ordinal = ordinal;
+        this.stateData = stateData;
+    }
+
+    @Override
+    public String typ() {
+        return "originalOutput1";
+    }
+
+    @Override
+    public void writeForHash(ByteArrayOutputStream out) {
+        mustWriteForHash(out, this.source);
+        mustWriteForHash(out, this.controlProgram);
+        mustWriteForHash(out, this.stateData);
+    }
+}
index 0d3a83f..6fa2273 100755 (executable)
@@ -2,36 +2,15 @@ package io.bytom.offline.types;
 \r
 import java.io.ByteArrayOutputStream;\r
 \r
-public class OutputEntry extends Entry {\r
+public abstract class OutputEntry extends Entry {\r
+    protected ValueSource source;\r
 \r
-    private ValueSource source;\r
+    protected Program controlProgram;\r
 \r
-    private Program controlProgram;\r
+    protected Integer ordinal;\r
 \r
-    private Integer ordinal;\r
+    protected byte[][] stateData;\r
 \r
-    public OutputEntry() {\r
-        this.source = new ValueSource();\r
-        this.controlProgram = new Program();\r
-    }\r
-\r
-\r
-    public OutputEntry(ValueSource source, Program controlProgram, Integer ordinal) {\r
-        this.source = source;\r
-        this.controlProgram = controlProgram;\r
-        this.ordinal = ordinal;\r
-    }\r
-\r
-    @Override\r
-    public String typ() {\r
-        return "output1";\r
-    }\r
-\r
-    @Override\r
-    public void writeForHash(ByteArrayOutputStream out) {\r
-        mustWriteForHash(out, this.source);\r
-        mustWriteForHash(out, this.controlProgram);\r
-    }\r
 \r
     public ValueSource getSource() {\r
         return source;\r
index 4ca1e9c..a6eb672 100755 (executable)
@@ -11,12 +11,18 @@ public class ValueSource {
 \r
     public ValueSource() {}\r
 \r
+    public ValueSource(Hash ref, AssetAmount value) {\r
+        this.ref = ref;\r
+        this.value = value;\r
+    }\r
+\r
     public ValueSource(Hash ref, AssetAmount value, long position) {\r
         this.ref = ref;\r
         this.value = value;\r
         this.position = position;\r
     }\r
 \r
+\r
     public Hash getRef() {\r
         return ref;\r
     }\r
diff --git a/tx-signer/src/main/java/io/bytom/offline/types/Veto.java b/tx-signer/src/main/java/io/bytom/offline/types/Veto.java
new file mode 100644 (file)
index 0000000..d45099b
--- /dev/null
@@ -0,0 +1,31 @@
+package io.bytom.offline.types;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Map;
+
+public class Veto extends InputEntry{
+    private Hash spentOutputID;
+    private ValueDestination witnessDestination;
+
+
+    public Veto(Hash spentOutputID, int ordinal) {
+        this.spentOutputID = spentOutputID;
+        this.ordinal = ordinal;
+    }
+
+    @Override
+    public String typ() {
+        return "vetoInput1";
+    }
+
+    @Override
+    public void writeForHash(ByteArrayOutputStream out) {
+        mustWriteForHash(out,this.spentOutputID);
+    }
+
+    @Override
+    public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) {
+        OutputEntry spendOutput = (OutputEntry) entryMap.get(this.spentOutputID);
+        this.witnessDestination = new ValueDestination(id, spendOutput.getSource().getValue(), pos);
+    }
+}
diff --git a/tx-signer/src/main/java/io/bytom/offline/types/Vote.java b/tx-signer/src/main/java/io/bytom/offline/types/Vote.java
new file mode 100644 (file)
index 0000000..f12f17a
--- /dev/null
@@ -0,0 +1,37 @@
+package io.bytom.offline.types;
+
+import java.io.ByteArrayOutputStream;
+
+public class Vote extends OutputEntry{
+    private byte[] vote;
+
+    public Vote(ValueSource source, Program controlProgram, Integer ordinal,byte[] vote, byte[][] stateData) {
+        this.source = source;
+        this.controlProgram = controlProgram;
+        this.ordinal = ordinal;
+        this.vote = vote;
+        this.stateData = stateData;
+    }
+
+    @Override
+    public String typ() {
+        return "voteOutput1";
+    }
+
+    @Override
+    public void writeForHash(ByteArrayOutputStream out) {
+        mustWriteForHash(out,this.source);
+        mustWriteForHash(out,this.controlProgram);
+        mustWriteForHash(out,this.vote);
+        mustWriteForHash(out,this.stateData);
+    }
+
+    public byte[] getVote() {
+        return vote;
+    }
+
+    public void setVote(byte[] vote) {
+        this.vote = vote;
+    }
+
+}
diff --git a/tx-signer/src/main/java/io/bytom/offline/util/AssetIdUtil.java b/tx-signer/src/main/java/io/bytom/offline/util/AssetIdUtil.java
new file mode 100644 (file)
index 0000000..56b4b7e
--- /dev/null
@@ -0,0 +1,20 @@
+package io.bytom.offline.util;
+
+import io.bytom.offline.types.AssetDefinition;
+import io.bytom.offline.types.Hash;
+import io.bytom.offline.types.Program;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.ByteArrayOutputStream;
+
+public class AssetIdUtil {
+    public static String computeAssetID(String rawAssetDefinition,String controlProgram){
+        byte[] hashBytes = SHA3Util.hashSha256(Hex.decode(rawAssetDefinition));
+        Hash assetDefHash = new Hash(hashBytes);
+        Program program = new Program(1,Hex.decode(controlProgram));
+        AssetDefinition assetDefinition = new AssetDefinition(assetDefHash, program);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        assetDefinition.writeForHash(out);
+        return Hex.toHexString(SHA3Util.hashSha256(out.toByteArray()));
+    }
+}
diff --git a/tx-signer/src/test/java/io/bytom/offline/api/AppTest.java b/tx-signer/src/test/java/io/bytom/offline/api/AppTest.java
deleted file mode 100755 (executable)
index 6897e92..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-package io.bytom.offline.api;\r
-\r
-import org.junit.Test;\r
-\r
-public class AppTest {\r
-    String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
-    String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
-\r
-\r
-    @Test\r
-    public void testSpendBIP44() {\r
-        SpendInput input = new SpendInput();\r
-        input.setAssetId(btmAssetID);\r
-        input.setAmount(9800000000L);\r
-        input.setProgram("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");\r
-        input.setSourcePosition(2);\r
-        input.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");\r
-        input.setChange(true);\r
-        input.setControlProgramIndex(457);\r
-        input.setKeyIndex(1);\r
-        input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");\r
-\r
-        Transaction tx = new Transaction.Builder()\r
-                .addInput(input)\r
-                .addOutput(new Output(btmAssetID, 8800000000L, "0014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c"))\r
-                .addOutput(new Output(btmAssetID, 900000000L, "00200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac66"))\r
-                .setTimeRange(0)\r
-                .build();\r
-\r
-        String rawTransaction = tx.rawTransaction();\r
-        assert rawTransaction.equals("070100010160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302401cb779288be890a28c5209036da1a27d9fe74a51c38e0a10db4817bcf4fd05f68580239eea7dcabf19f144c77bf13d3674b5139aa51a99ba58118386c190af0e20bcbe020b05e1b7d0825953d92bf47897be08cd7751a37adb95d6a2e5224f55ab02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80b095e42001160014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c000149ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d293ad03012200200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac6600");\r
-    }\r
-\r
-    @Test\r
-    public void testSpendBIP32() {\r
-        SpendInput input = new SpendInput(btmAssetID, 11718900000L, "0014085a02ecdf934a56343aa59a3dec9d9feb86ee43");\r
-        input.setSourceID("5ac79a73db78e5c9215b37cb752f0147d1157c542bb4884908ceb97abc33fe0a");\r
-        input.setSourcePosition(0);\r
-        input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");\r
-        input.setChange(true);\r
-        input.setKeyIndex(1);\r
-        input.setBipProtocol(BIPProtocol.BIP32);\r
-        input.setControlProgramIndex(447);\r
-\r
-        Transaction tx = new Transaction.Builder()\r
-                .addInput(input)\r
-                .addOutput(new Output(btmAssetID, 1718900000L, "001409a0961e9b592a944ca3ded0ef9403fdb25b3793"))\r
-                .addOutput(new Output(btmAssetID, 9900000000L, "00145ade29df622cc68d0473aa1a20fb89690451c66e"))\r
-                .setTimeRange(0)\r
-                .build();\r
-\r
-        String rawTx = tx.rawTransaction();\r
-        assert rawTx.equals("070100010160015e5ac79a73db78e5c9215b37cb752f0147d1157c542bb4884908ceb97abc33fe0affffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0f280d42b0001160014085a02ecdf934a56343aa59a3dec9d9feb86ee43630240e37864ef905e943deb97e58b861c97f04f07c1d33b1ce4f1b6edddff0c8956a57d86fc922c45446cd38ea613fc3c9b7d5a2596f3dca7a7212f6da55b9515110420a29601468f08c57ca9c383d28736a9d5c7737cd483126d8db3d85490fe497b3502013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0aad1b3060116001409a0961e9b592a944ca3ded0ef9403fdb25b379300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8086d8f024011600145ade29df622cc68d0473aa1a20fb89690451c66e00");\r
-    }\r
-\r
-    //issue asset\r
-    @Test\r
-    public void testIssue() {\r
-        IssuanceInput issuanceInput = new IssuanceInput();\r
-        issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");\r
-        issuanceInput.setAmount(100000000000L);\r
-        issuanceInput.setProgram("ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad");\r
-        issuanceInput.setNonce("ac9d5a527f5ab00a");\r
-        issuanceInput.setKeyIndex(5);\r
-        issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d");\r
-        issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");\r
-\r
-        SpendInput spendInput = new SpendInput(btmAssetID, 9800000000L, "0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");\r
-        spendInput.setBipProtocol(BIPProtocol.BIP32);\r
-        spendInput.setKeyIndex(1);\r
-        spendInput.setChange(true);\r
-        spendInput.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");\r
-        spendInput.setSourcePosition(2);\r
-        spendInput.setControlProgramIndex(457);\r
-        spendInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");\r
-\r
-        Transaction tx = new Transaction.Builder()\r
-                .addInput(issuanceInput)\r
-                .addInput(spendInput)\r
-                .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449"))\r
-                .addOutput(new Output(btmAssetID, 9700000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56"))\r
-                .setTimeRange(0)\r
-                .build();\r
-\r
-        String rawTx = tx.rawTransaction();\r
-        assert rawTx.equals("0701000201300008ac9d5a527f5ab00a7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f402b001467b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d0125ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad01401ba3a3f2d3e9887e20da8d43666cc1602bb5421ffea9d2b4d7df9816fc174a43b25fb00db4775b10b679a8b5f8aa28555ebb8fb49f6275d43283daf8f5ac340d0160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e630240341c875fc815dc13ad811c9aa7c7af28a5c78765f77fd5a36dac263278fd605ae0553e34a1e645148370b7b397142ddbcd67ba33483279ab9894169b51e4f101201381d35e235813ad1e62f9a602c82abee90565639cc4573568206b55bcd2aed902013e7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f4020116001437e1aec83a4e6587ca9609e4e5aa728db700744900013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8082a99124011600148be1104e04734e5edaba5eea2e85793896b77c5600");\r
-    }\r
-\r
-\r
-    //retire asset\r
-    @Test\r
-    public void testRetire() {\r
-        String arbitrary = "77656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c";\r
-        String assetId1 = "207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf";\r
-\r
-        SpendInput input1 = new SpendInput(btmAssetID, 289100000L, "0014f1dc52048f439ac7fd74f8106a21da78f00de48f");\r
-        input1.setRootPrivateKey(rootKey);\r
-        input1.setChange(true);\r
-        input1.setControlProgramIndex(41);\r
-        input1.setSourceID("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65");\r
-        input1.setKeyIndex(1);\r
-        input1.setSourcePosition(0);\r
-\r
-        SpendInput input2 = new SpendInput(assetId1, 70000000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf");\r
-        input2.setRootPrivateKey(rootKey);\r
-        input2.setChange(true);\r
-        input2.setControlProgramIndex(26);\r
-        input2.setSourceID("be0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b");\r
-        input2.setKeyIndex(1);\r
-        input2.setSourcePosition(1);\r
-\r
-        Output output1 = new Output(btmAssetID, 279100000L, "001414d362694eacfa110dc20dec77d610d22340f95b");\r
-        Output output2 = Output.newRetireOutput(assetId1, 10000000000L, arbitrary);\r
-        Output output3 = new Output(assetId1, 60000000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf");\r
-\r
-        Transaction transaction = new Transaction.Builder()\r
-                .addInput(input1)\r
-                .addInput(input2)\r
-                .addOutput(output1)\r
-                .addOutput(output2)\r
-                .addOutput(output3)\r
-                .setTimeRange(2000000)\r
-                .build();\r
-\r
-        String rawTransaction = transaction.rawTransaction();\r
-        assert  rawTransaction.equals("070180897a020160015e0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0a1ed89010001160014f1dc52048f439ac7fd74f8106a21da78f00de48f6302401660121218ab96d9f22cce712541ca34c53f4da40450669854341ca9624ad1cf10d1bfc96449fad5406224afd253ccfbdeab683f7ec7f9ee8f45e47a0c58500f2031ecc1bdd5fb9b40016358340b87646ea39faf55c0c105205cfdfdc6184725f40161015fbe0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80f8cce284020101160014bb8a039726df1b649738e9973db14a4b4fd4becf630240d7b7f1c2ca1048fd6798234f2a1e895762f83e802507a008eff52605611b67390a74eaf228b76f5589ff109b2c20eaa65fad6de2e5ab8a25b54267b607df970b20a71547e1064b5edaad92cdce6b0ace832836ba28fdeaf0b83010bed247fe927c03013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f48a85010116001414d362694eacfa110dc20dec77d610d22340f95b00014b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80c8afa02501246a2277656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c00013e207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80b09dc2df0101160014bb8a039726df1b649738e9973db14a4b4fd4becf00");\r
-    }\r
-}\r
diff --git a/tx-signer/src/test/java/io/bytom/offline/api/TransactionTest.java b/tx-signer/src/test/java/io/bytom/offline/api/TransactionTest.java
new file mode 100644 (file)
index 0000000..e351a19
--- /dev/null
@@ -0,0 +1,74 @@
+package io.bytom.offline.api;
+
+import io.bytom.offline.util.AssetIdUtil;
+import org.junit.Test;
+
+public class TransactionTest {
+    String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
+
+    @Test
+    public void testIssuance() {
+        String rawAssetDefinition = "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022676f6c64222c0a20202271756f72756d223a20312c0a20202272656973737565223a202266616c7365222c0a20202273796d626f6c223a2022474f4c44220a7d";
+        String issuanceProgram = "0204bfcda069ae2000cf50143dac994b9cfc1a2c45a4332ecefbbb15824b9f1a537f0b49fd9f44b45151ad";
+        System.out.println(AssetIdUtil.computeAssetID(rawAssetDefinition,issuanceProgram));
+        IssuanceInput issuanceInput = new IssuanceInput("0541db69c21dc827092ddbc5673f5c1f0a09d3112da2a67c6644ec1be3fa38b3",2543541111111L,"0204bfcda069ae2000cf50143dac994b9cfc1a2c45a4332ecefbbb15824b9f1a537f0b49fd9f44b45151ad");
+        issuanceInput.setVmVersion(1);
+        issuanceInput.setRootPrivateKey("b0cc39f8a4b9539fcc8f05c0df21563155767bfc6f2c4b801738eb831589d84afe549d3a89e8223762445a0f3bea5c192675636846f75f1455c6a23d30a37023");
+        issuanceInput.setKeyIndex(3);
+        issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022676f6c64222c0a20202271756f72756d223a20312c0a20202272656973737565223a202266616c7365222c0a20202273796d626f6c223a2022474f4c44220a7d");
+        issuanceInput.setNonce("");
+
+        SpendInput spendInput = new SpendInput();
+        spendInput.setAssetId(btmAssetID);
+        spendInput.setAmount(2853881270l);
+        spendInput.setSourceID("0b75ee155f60160ac14e038eaf7c67c820d77096a615ab00c1a3d9b52f9f246c");
+        spendInput.setSourcePosition(0);
+        spendInput.setChange(false);
+        spendInput.setVmVersion(1);
+        spendInput.setProgram("0014e933e5a0545c63ff2c28eaedb47c831cac3b16cf");
+        spendInput.setControlProgramIndex(2);
+        spendInput.setRootPrivateKey("b0cc39f8a4b9539fcc8f05c0df21563155767bfc6f2c4b801738eb831589d84afe549d3a89e8223762445a0f3bea5c192675636846f75f1455c6a23d30a37023");
+        spendInput.setKeyIndex(1);
+
+        OriginalOutput issuanceOutput = new OriginalOutput("0541db69c21dc827092ddbc5673f5c1f0a09d3112da2a67c6644ec1be3fa38b3",2543541111111L,"00145894d753c19d8fccce04db88f54751340ad8ca4f");
+        OriginalOutput output = new OriginalOutput(btmAssetID,2753881270L,"0014b0b9455c1f77476b96858927d98e823d680ce889");
+
+        Transaction tx = new Transaction.Builder()
+                .addInput(issuanceInput)
+                .addInput(spendInput)
+                .addOutput(issuanceOutput)
+                .addOutput(output)
+                .setTimeRange(0)
+                .setSize(0)
+                .build();
+
+        String rawTransaction = tx.rawTransaction();
+        System.out.println(rawTransaction);
+    }
+
+    @Test
+    public void testSpendBIP44() {
+        SpendInput spendInput = new SpendInput();
+        spendInput.setAssetId(btmAssetID);
+        spendInput.setAmount(2873881270l);
+        spendInput.setSourceID("6dd143552ff8fde887e65eb7c387ff77265d270dfc76d6488a0185476f66c601");
+        spendInput.setSourcePosition(0);
+        spendInput.setChange(false);
+        spendInput.setVmVersion(1);
+        spendInput.setProgram("0014e933e5a0545c63ff2c28eaedb47c831cac3b16cf");
+        spendInput.setControlProgramIndex(2);
+        spendInput.setRootPrivateKey("b0cc39f8a4b9539fcc8f05c0df21563155767bfc6f2c4b801738eb831589d84afe549d3a89e8223762445a0f3bea5c192675636846f75f1455c6a23d30a37023");
+        spendInput.setKeyIndex(1);
+
+
+        OriginalOutput output = new OriginalOutput(btmAssetID,2773881270l,"0014b0b9455c1f77476b96858927d98e823d680ce889");
+        Transaction tx = new Transaction.Builder()
+                .addInput(spendInput)
+                .addOutput(output)
+                .setTimeRange(0)
+                .setSize(0)
+                .build();
+        System.out.println(tx.rawTransaction());
+        System.out.println(tx.getTxID());
+    }
+}
diff --git a/tx-signer/src/test/java/io/bytom/offline/api/TxInputTest.java b/tx-signer/src/test/java/io/bytom/offline/api/TxInputTest.java
new file mode 100644 (file)
index 0000000..22bfffa
--- /dev/null
@@ -0,0 +1,147 @@
+package io.bytom.offline.api;
+
+import com.amazonaws.util.StringUtils;
+import com.google.crypto.tink.subtle.Hex;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+public class TxInputTest {
+    @Test
+    public void testSpendInput() throws IOException {
+        SpendInput input = new SpendInput();
+        input.setAssetId("fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a");
+        input.setAmount(254354);
+        input.setSourceID("fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409");
+        input.setSourcePosition(3);
+        input.setVmVersion(1);
+        input.setProgram(Hex.encode("spendProgram".getBytes()));
+        input.appendStateData(Hex.encode("stateData".getBytes()));
+        input.appendWitnessComponent(Hex.encode("arguments1".getBytes()));
+        input.appendWitnessComponent(Hex.encode("arguments2".getBytes()));
+
+        byte[] serializeInputCommitment = input.serializeInput();
+        String wantStr= StringUtils.join("",
+                "01", // asset version
+                "5f", // input commitment length
+                "01", // spend type flag
+                "5d", // spend commitment length
+                "fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409", // source id
+                "fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a", // assetID
+                "92c30f",                   // amount
+                "03",                       // source position
+                "01",                       // vm version
+                "0c",                       // spend program length
+                "7370656e6450726f6772616d", // spend program
+                "0109",                     // state length
+                "737461746544617461",       // state
+                "17",                       // witness length
+                "02",                       // argument array length
+                "0a",                       // first argument length
+                "617267756d656e747331",     // first argument data
+                "0a",                       // second argument length
+                "617267756d656e747332"      // second argument data
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeInputCommitment));
+    }
+
+    @Test
+    public void testIssuanceInput() throws IOException {
+        IssuanceInput issuanceInput = new IssuanceInput();
+        issuanceInput.setAmount(254354L);
+        issuanceInput.setVmVersion(1);
+        issuanceInput.setProgram(Hex.encode("issuanceProgram".getBytes()));
+        issuanceInput.setNonce(Hex.encode("nonce".getBytes()));
+        issuanceInput.setRawAssetDefinition(Hex.encode("assetDefinition".getBytes()));
+        issuanceInput.appendWitnessComponent(Hex.encode("arguments1".getBytes()));
+        issuanceInput.appendWitnessComponent(Hex.encode("arguments2".getBytes()));
+
+        byte[] serializeInputCommitment = issuanceInput.serializeInput();
+        String wantStr= StringUtils.join("",
+                "01",         // asset version
+                "2a",         // serialization length
+                "00",         // issuance type flag
+                "05",         // nonce length
+                "6e6f6e6365", // nonce
+                "a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf", // assetID
+                "92c30f",                         // amount
+                "38",                             // input witness length
+                "0f",                             // asset definition length
+                "6173736574446566696e6974696f6e", // asset definition
+                "01",                             // vm version
+                "0f",                             // issuanceProgram length
+                "69737375616e636550726f6772616d", // issuance program
+                "02",                             // argument array length
+                "0a",                             // first argument length
+                "617267756d656e747331",           // first argument data
+                "0a",                             // second argument length
+                "617267756d656e747332"            // second argument data
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeInputCommitment));
+    }
+
+    @Test
+    public void testVetoInput() throws IOException {
+        VetoInput vetoInput = new VetoInput();
+        vetoInput.setSourceID("fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409");
+        vetoInput.setAssetId("fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a");
+        vetoInput.setSourcePosition(3);
+        vetoInput.setAmount(254354L);
+        vetoInput.setVmVersion(1);
+        vetoInput.setProgram(Hex.encode("spendProgram".getBytes()));
+        vetoInput.setVote("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269".getBytes());
+        vetoInput.appendWitnessComponent(Hex.encode("arguments1".getBytes()));
+        vetoInput.appendWitnessComponent(Hex.encode("arguments2".getBytes()));
+
+
+        byte[] serializeInputCommitment = vetoInput.serializeInput();
+
+        String wantStr= StringUtils.join("",
+                "01",   // asset version
+                "d701", // input commitment length
+                "03",   // veto type flag
+                "53",   // veto commitment length
+                "fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409", // source id
+                "fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a", // assetID
+                "92c30f",                   // amount
+                "03",                       // source position
+                "01",                       // vm version
+                "0c",                       // veto program length
+                "7370656e6450726f6772616d", // veto program
+                "00",                       // state length
+                "8001",                     //xpub length
+                "6166353934303036613430383337643966303238646161626236643538396466306239313338646165666164353638336535323333633236343632373932313732393461386435333265363038363362636631393636323561333566623863656566666133633039363130656239326463666236353561393437663133323639", //voter xpub
+                "17",                   // witness length
+                "02",                   // argument array length
+                "0a",                   // first argument length
+                "617267756d656e747331", // first argument data
+                "0a",                   // second argument length
+                "617267756d656e747332" // second argument data
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeInputCommitment));
+    }
+
+    @Test
+    public void testCoinbaseInput() throws IOException {
+        CoinbaseInput coinbaseInput = new CoinbaseInput();
+        coinbaseInput.setArbitrary(Hex.encode("arbitrary".getBytes()));
+
+        byte[] serializeInputCommitment = coinbaseInput.serializeInput();
+
+        String wantStr= StringUtils.join("",
+                "01",                 // asset version
+                "0b",                 // input commitment length
+                "02",                 // coinbase type flag
+                "09",                 // arbitrary length
+                "617262697472617279", // arbitrary data
+                "00"                // witness length
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeInputCommitment));
+    }
+}
diff --git a/tx-signer/src/test/java/io/bytom/offline/api/TxOutputTest.java b/tx-signer/src/test/java/io/bytom/offline/api/TxOutputTest.java
new file mode 100644 (file)
index 0000000..8bd83d2
--- /dev/null
@@ -0,0 +1,58 @@
+package io.bytom.offline.api;
+
+import com.amazonaws.util.StringUtils;
+import com.google.crypto.tink.subtle.Hex;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+public class TxOutputTest {
+    String assetID = "81756fdab39a17163b0ce582ee4ee256fb4d1e156c692b997d608a42ecb38d47";
+    @Test
+    public void  SerializationOriginalTxOutputTest() throws IOException {
+        OriginalOutput output = new OriginalOutput(assetID,254354L,Hex.encode("TestSerializationTxOutput".getBytes()));
+        output.appendStateData(Hex.encode("stateData".getBytes()));
+
+        byte[] serializeOutputCommitment = output.serializeOutput();
+        String wantStr= StringUtils.join("",
+                "01", // asset version
+                "00", // output type
+                "49", // serialization length
+                "81756fdab39a17163b0ce582ee4ee256fb4d1e156c692b997d608a42ecb38d47", // assetID
+                "92c30f", // amount
+                "01",     // version
+                "19",     // control program length
+                "5465737453657269616c697a6174696f6e54784f7574707574", // control program
+                "0109",               // state data length
+                "737461746544617461", // state data
+                "00"                 // witness length
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeOutputCommitment));
+    }
+
+    @Test
+    public void  SerializationVetoOutputTest() throws IOException {
+        OriginalOutput output = new OriginalOutput(assetID,254354L,Hex.encode("TestSerializationTxOutput".getBytes()));
+        output.appendStateData(Hex.encode("stateData".getBytes()));
+
+        byte[] serializeOutputCommitment = output.serializeOutput();
+        String wantStr= StringUtils.join("",
+                "01", // asset version
+                "00", // output type
+                "49", // serialization length
+                "81756fdab39a17163b0ce582ee4ee256fb4d1e156c692b997d608a42ecb38d47", // assetID
+                "92c30f", // amount
+                "01",     // version
+                "19",     // control program length
+                "5465737453657269616c697a6174696f6e54784f7574707574", // control program
+                "0109",               // state data length
+                "737461746544617461", // state data
+                "00"                 // witness length
+        );
+
+        assertEquals(wantStr,Hex.encode(serializeOutputCommitment));
+    }
+}