OSDN Git Service

refactor
authorshenao78 <shenao.78@163.com>
Wed, 20 Mar 2019 06:53:39 +0000 (14:53 +0800)
committershenao78 <shenao.78@163.com>
Wed, 20 Mar 2019 06:53:39 +0000 (14:53 +0800)
32 files changed:
tx-signer/README.md
tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519.java
tx-signer/src/main/java/io/bytom/api/BIPProtocol.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/api/BaseInput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/api/IssuanceInput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/api/MapTransaction.java [deleted file]
tx-signer/src/main/java/io/bytom/api/Output.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/api/SignTransaction.java [deleted file]
tx-signer/src/main/java/io/bytom/api/Signer.java
tx-signer/src/main/java/io/bytom/api/SpendInput.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/api/Transaction.java
tx-signer/src/main/java/io/bytom/api/TransactionSigner.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/UTXO.java
tx-signer/src/main/java/io/bytom/api/WitnessComponent.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/common/Constants.java [deleted file]
tx-signer/src/main/java/io/bytom/common/DerivePrivateKey.java
tx-signer/src/main/java/io/bytom/common/DeriveXpub.java
tx-signer/src/main/java/io/bytom/common/ExpandedPrivateKey.java
tx-signer/src/main/java/io/bytom/common/NonHardenedChild.java
tx-signer/src/main/java/io/bytom/common/Utils.java
tx-signer/src/main/java/io/bytom/common/VMUtil.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/types/Asset.java
tx-signer/src/main/java/io/bytom/types/InputEntry.java [new file with mode: 0644]
tx-signer/src/main/java/io/bytom/types/Issue.java
tx-signer/src/main/java/io/bytom/types/OutputEntry.java [moved from tx-signer/src/main/java/io/bytom/types/Output.java with 76% similarity]
tx-signer/src/main/java/io/bytom/types/Spend.java
tx-signer/src/main/java/io/bytom/util/PathUtil.java
tx-signer/src/test/java/io/bytom/AppTest.java
tx-signer/src/test/java/io/bytom/api/SignerTest.java
tx-signer/src/test/java/io/bytom/common/DerivePrivateKeyTest.java
tx-signer/src/test/java/io/bytom/common/ExpandedPrivateKeyTest.java
tx-signer/src/test/java/io/bytom/common/NonHardenedChildTest.java

index 10b8c24..ae213ae 100755 (executable)
@@ -76,7 +76,7 @@ Need 3 Parameters:
 \r
 ```java\r
 \r
-//utxo json to input public Transaction.AnnotatedInput btmUtxoToInput() {  \r
+AnnotatedBaseInput\r
     String utxoJson = "{\n" +  \r
             " \"id\": \"cf2f5c7340490d33d535a680dc8d95bb66fcccbf1045706484621cc067b982ae\",\n" +  \r
             " \"amount\": 70000000,\n" +  \r
index 6199c0a..8eed9b0 100755 (executable)
@@ -889,7 +889,7 @@ public final class Ed25519 {
      * Input:\r
      * s[0]+256*s[1]+...+256^63*s[63] = s\r
      * <p>\r
-     * Output:\r
+     * OutputEntry:\r
      * s[0]+256*s[1]+...+256^31*s[31] = s mod l\r
      * where l = 2^252 + 27742317777372353535851937790883648493.\r
      * Overwrites s in place.\r
@@ -1250,7 +1250,7 @@ public final class Ed25519 {
      * b[0]+256*b[1]+...+256^31*b[31] = b\r
      * c[0]+256*c[1]+...+256^31*c[31] = c\r
      * <p>\r
-     * Output:\r
+     * OutputEntry:\r
      * s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l\r
      * where l = 2^252 + 27742317777372353535851937790883648493.\r
      */\r
diff --git a/tx-signer/src/main/java/io/bytom/api/BIPProtocol.java b/tx-signer/src/main/java/io/bytom/api/BIPProtocol.java
new file mode 100644 (file)
index 0000000..c66091d
--- /dev/null
@@ -0,0 +1,7 @@
+package io.bytom.api;
+
+public enum BIPProtocol {
+
+    BIP32,
+    BIP44
+}
diff --git a/tx-signer/src/main/java/io/bytom/api/BaseInput.java b/tx-signer/src/main/java/io/bytom/api/BaseInput.java
new file mode 100644 (file)
index 0000000..1fb421d
--- /dev/null
@@ -0,0 +1,107 @@
+package io.bytom.api;
+
+import io.bytom.common.Utils;
+import io.bytom.types.*;
+import java.io.IOException;
+import java.util.Map;
+
+public abstract class BaseInput {
+
+    public static final int ISSUANCE_INPUT_TYPE = 0;
+    public static final int SPEND_INPUT_TYPE = 1;
+
+    private String inputID;
+
+    /**
+     * The number of units of the asset being issued or spent.
+     */
+    private long amount;
+
+    /**
+     * The id of the asset being issued or spent.
+     */
+    private String assetId;
+
+    /**
+     * The program which must be satisfied to transfer this output.
+     */
+    private String program;
+
+    private int vmVersion;
+
+    private int keyIndex;
+
+    private WitnessComponent witnessComponent = new WitnessComponent();
+
+    public BaseInput() {
+        this.vmVersion = 1;
+    }
+
+    public abstract InputEntry convertInputEntry(Map<Hash, Entry> entryMap, int index);
+
+    public abstract byte[] serializeInput() throws IOException;
+
+    public abstract void buildWitness(String txID) throws Exception;
+
+    @Override
+    public String toString() {
+        return Utils.serializer.toJson(this);
+    }
+
+
+    public AssetAmount getAssetAmount() {
+        return new AssetAmount(new AssetID(this.assetId), this.amount);
+    }
+
+    public void setRootPrivateKey(String prvKey) {
+        witnessComponent.setRootPrivateKey(prvKey);
+    }
+
+    public String getInputID() {
+        return inputID;
+    }
+
+    public int getKeyIndex() {
+        return keyIndex;
+    }
+
+    public int getVmVersion() {
+        return vmVersion;
+    }
+
+    public String getProgram() {
+        return program;
+    }
+
+    public String getAssetId() {
+        return assetId;
+    }
+
+    public long getAmount() {
+        return amount;
+    }
+
+    public void setInputID(String inputID) {
+        this.inputID = inputID;
+    }
+
+    public void setAmount(long amount) {
+        this.amount = amount;
+    }
+
+    public void setAssetId(String assetId) {
+        this.assetId = assetId;
+    }
+
+    public void setProgram(String program) {
+        this.program = program;
+    }
+
+    public void setKeyIndex(int keyIndex) {
+        this.keyIndex = keyIndex;
+    }
+
+    public WitnessComponent getWitnessComponent() {
+        return witnessComponent;
+    }
+}
diff --git a/tx-signer/src/main/java/io/bytom/api/IssuanceInput.java b/tx-signer/src/main/java/io/bytom/api/IssuanceInput.java
new file mode 100644 (file)
index 0000000..cc3e90f
--- /dev/null
@@ -0,0 +1,104 @@
+package io.bytom.api;
+
+import io.bytom.common.DerivePrivateKey;
+import io.bytom.common.ExpandedPrivateKey;
+import io.bytom.common.Utils;
+import io.bytom.types.*;
+import io.bytom.util.SHA3Util;
+import org.bouncycastle.util.encoders.Hex;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Map;
+
+public class IssuanceInput extends BaseInput {
+
+    private String nonce;
+
+    private String assetDefinition;
+
+    public IssuanceInput() {}
+
+    @Override
+    public InputEntry convertInputEntry(Map<Hash, Entry> entryMap, int index) {
+        if (this.nonce == null) {
+            SecureRandom sr = new SecureRandom();
+            byte[] randBytes = new byte[8];
+            sr.nextBytes(randBytes);
+            this.nonce = Hex.toHexString(randBytes);
+        }
+
+        Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(this.nonce)));
+        Hash assetDefHash = new Hash(this.assetDefinition);
+        AssetAmount value = this.getAssetAmount();
+
+        Issue issuance = new Issue(nonceHash, value, index);
+        Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram()));
+        issuance.assetDefinition = new AssetDefinition(assetDefHash, pro);
+        return issuance;
+    }
+
+    @Override
+    public byte[] serializeInput() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        //assetVersion
+        Utils.writeVarint(1, stream);
+        //写入 type nonce assetId amount
+        ByteArrayOutputStream issueInfo = new ByteArrayOutputStream();
+        //写入 input.type==00 issue
+        Utils.writeVarint(ISSUANCE_INPUT_TYPE, issueInfo);
+        //写入 8个字节的随机数
+        Utils.writeVarStr(Hex.decode(nonce), issueInfo);
+        issueInfo.write(Hex.decode(getAssetId()));
+        Utils.writeVarint(getAmount(), issueInfo);
+        stream.write(issueInfo.toByteArray().length);
+        stream.write(issueInfo.toByteArray());
+
+        ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream();
+        //未知
+        Utils.writeVarint(1, issueInfo1);
+        //写入assetDefine
+        Utils.writeVarStr(Hex.decode(assetDefinition), issueInfo1);
+        //vm.version
+        Utils.writeVarint(1, issueInfo1);
+        //controlProgram
+        Utils.writeVarStr(Hex.decode(getProgram()), issueInfo1);
+
+        //inputWitness
+        if (null != getWitnessComponent()) {
+            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();
+            //arguments
+            int witnessSize = getWitnessComponent().size();
+            //arguments的length
+            Utils.writeVarint(witnessSize, witnessStream);
+            for (int i = 0; i < witnessSize; i++) {
+                String witness = getWitnessComponent().getWitness(i);
+                Utils.writeVarStr(Hex.decode(witness), witnessStream);
+            }
+            issueInfo1.write(witnessStream.toByteArray());
+        }
+        stream.write(issueInfo1.toByteArray().length - 1);
+        stream.write(issueInfo1.toByteArray());
+        return stream.toByteArray();
+    }
+
+    @Override
+    public void buildWitness(String txID) throws Exception {
+        String rootPrivateKey = getWitnessComponent().getRootPrivateKey();
+        byte[] childPrivateKey = DerivePrivateKey.bip32derivePrvKey(rootPrivateKey, getKeyIndex(), TransactionSigner.AssetKeySpace);
+
+        byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID));
+        byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(childPrivateKey);
+        byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message);
+
+        getWitnessComponent().appendWitness(Hex.toHexString(sig));
+    }
+
+    public void setNonce(String nonce) {
+        this.nonce = nonce;
+    }
+
+    public void setAssetDefinition(String assetDefinition) {
+        this.assetDefinition = assetDefinition;
+    }
+}
diff --git a/tx-signer/src/main/java/io/bytom/api/MapTransaction.java b/tx-signer/src/main/java/io/bytom/api/MapTransaction.java
deleted file mode 100755 (executable)
index 349d33c..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-package io.bytom.api;\r
-\r
-import io.bytom.types.*;\r
-import io.bytom.util.SHA3Util;\r
-import org.bouncycastle.util.encoders.Hex;\r
-\r
-import java.security.SecureRandom;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-public class MapTransaction {\r
-\r
-    public static Transaction mapTx(Transaction tx) {\r
-        Map<Hash, Entry> entryMap = new HashMap<>();\r
-        ValueSource[] muxSources = new ValueSource[tx.inputs.size()];\r
-        List<Spend> spends = new ArrayList<>();\r
-        List<Issue> issuances = new ArrayList<>();\r
-        try {\r
-            for (int i = 0; i < tx.inputs.size(); i++) {\r
-                Transaction.AnnotatedInput input = tx.inputs.get(i);\r
-                switch (input.type) {\r
-                    case 1: {\r
-                        Program pro = new Program(input.type, Hex.decode(input.controlProgram));\r
-                        AssetID assetID = new AssetID(input.assetId);\r
-                        AssetAmount assetAmount = new AssetAmount(assetID, input.amount);\r
-                        Hash sourceID = new Hash(input.sourceId);\r
-                        ValueSource src = new ValueSource(sourceID, assetAmount, input.sourcePosition);\r
-\r
-                        Output prevout = new Output(src, pro, 0);\r
-                        Hash prevoutID = addEntry(entryMap, prevout);\r
-                        Spend spend = new Spend(prevoutID, i);\r
-                        Hash spendID = addEntry(entryMap, spend);\r
-                        input.inputID = spendID.toString();\r
-\r
-                        muxSources[i] = new ValueSource(spendID, assetAmount, 0);\r
-                        spends.add(spend);\r
-                        break;\r
-                    }\r
-                    case 0: {\r
-                        if (input.nonce == null) {\r
-                            SecureRandom sr = new SecureRandom();\r
-                            byte[] randBytes = new byte[8];\r
-                            sr.nextBytes(randBytes);\r
-                            input.nonce = Hex.toHexString(randBytes);\r
-                        }\r
-                        Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(input.nonce)));\r
-                        Hash assetDefHash = new Hash(input.assetDefinition);\r
-                        AssetID assetID = new AssetID(input.assetId);\r
-                        AssetAmount value = new AssetAmount(assetID, input.amount);\r
-\r
-                        Issue issuance = new Issue(nonceHash, value, i);\r
-                        Program pro = new Program(input.type, Hex.decode(input.controlProgram));\r
-                        issuance.assetDefinition = new AssetDefinition(assetDefHash, pro);\r
-                        Hash issuanceID = addEntry(entryMap, issuance);\r
-                        input.inputID = issuanceID.toString();\r
-                        muxSources[i] = new ValueSource(issuanceID, value, 0);\r
-                        issuances.add(issuance);\r
-                        break;\r
-                    }\r
-                }\r
-            }\r
-            Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));\r
-            Hash muxID = addEntry(entryMap, mux);\r
-            for (Spend spend : spends) {\r
-                Output spendOutput = (Output) entryMap.get(spend.spentOutputID);\r
-                spend.setDestination(muxID, spendOutput.source.value, spend.ordinal);\r
-            }\r
-            for (Issue issue : issuances) {\r
-                issue.setDestination(muxID, issue.assetAmount, issue.ordinal);\r
-            }\r
-\r
-            List<Hash> resultIDList = new ArrayList<>();\r
-            for (int i = 0; i < tx.outputs.size(); i++) {\r
-                Transaction.AnnotatedOutput output = tx.outputs.get(i);\r
-\r
-                AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount);\r
-                ValueSource src = new ValueSource(muxID, amount, i);\r
-                Hash resultID;\r
-                if (output.controlProgram.startsWith("6a")&&output.controlProgram.length()>0) {\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
-                    Output oup = new Output(src, prog, i);\r
-                    resultID = addEntry(entryMap, oup);\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(tx.version, tx.size, tx.timeRange, resultIDList.toArray(new Hash[]{}));\r
-            Hash txID = addEntry(entryMap, txHeader);\r
-            tx.txID = txID.toString();\r
-        } catch (Exception e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-        return tx;\r
-    }\r
-\r
-    private static Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {\r
-        Hash id = entry.entryID();\r
-        entryMap.put(id, entry);\r
-        return id;\r
-    }\r
-}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/Output.java b/tx-signer/src/main/java/io/bytom/api/Output.java
new file mode 100644 (file)
index 0000000..7d3d4ba
--- /dev/null
@@ -0,0 +1,108 @@
+package io.bytom.api;
+
+import io.bytom.common.Utils;
+import org.bouncycastle.util.encoders.Hex;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class Output {
+
+    /**
+     * address
+     */
+    public String address;
+
+    /**
+     * The number of units of the asset being controlled.
+     */
+    public long amount;
+
+    /**
+     * The definition of the asset being controlled (possibly null).
+     */
+    public Map<String, Object> assetDefinition;
+
+    /**
+     * The id of the asset being controlled.
+     */
+    public String assetId;
+
+    /**
+     * The control program which must be satisfied to transfer this output.
+     */
+    public String controlProgram;
+
+    /**
+     * The id of the output.
+     */
+    public String id;
+
+    /**
+     * The output's position in a transaction's list of outputs.
+     */
+    public Integer position;
+
+    public Output(String assetId, long amount, String controlProgram) {
+        this.assetId = assetId;
+        this.amount = amount;
+        this.controlProgram = controlProgram;
+    }
+
+    public byte[] serializeOutput() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+        //assetVersion
+        Utils.writeVarint(1, stream); //AssetVersion是否默认为1
+        //outputCommit
+        ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();
+        //assetId
+        outputCommitSteam.write(Hex.decode(assetId));
+        //amount
+        Utils.writeVarint(amount, outputCommitSteam);
+        //vmVersion
+        Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version
+        //controlProgram
+        Utils.writeVarStr(Hex.decode(controlProgram), outputCommitSteam);
+
+        byte[] dataOutputCommit = outputCommitSteam.toByteArray();
+        //outputCommit的length
+        Utils.writeVarint(dataOutputCommit.length, stream);
+        stream.write(dataOutputCommit);
+
+        //outputWitness
+        Utils.writeVarint(0, stream);
+        return stream.toByteArray();
+    }
+
+    /**
+     * The type the output.<br>
+     * Possible values are "control" and "retire".
+     */
+    public String type;
+
+    public Output setAddress(String address) {
+        this.address = address;
+        return this;
+    }
+
+    public Output setAmount(long amount) {
+        this.amount = amount;
+        return this;
+    }
+
+    public Output setAssetId(String assetId) {
+        this.assetId = assetId;
+        return this;
+    }
+
+    public Output setControlProgram(String controlProgram) {
+        this.controlProgram = controlProgram;
+        return this;
+    }
+
+    public Output setPosition(Integer position) {
+        this.position = position;
+        return this;
+    }
+}
diff --git a/tx-signer/src/main/java/io/bytom/api/SignTransaction.java b/tx-signer/src/main/java/io/bytom/api/SignTransaction.java
deleted file mode 100755 (executable)
index 2cde88d..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-package io.bytom.api;\r
-\r
-import io.bytom.common.DerivePrivateKey;\r
-import io.bytom.common.DeriveXpub;\r
-import io.bytom.common.ExpandedPrivateKey;\r
-import io.bytom.common.Utils;\r
-import org.bouncycastle.jcajce.provider.digest.SHA3;\r
-import org.bouncycastle.util.encoders.Hex;\r
-\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.math.BigInteger;\r
-import java.security.InvalidKeyException;\r
-import java.security.NoSuchAlgorithmException;\r
-import java.security.SignatureException;\r
-\r
-\r
-/**\r
- * Created by liqiang on 2018/10/24.\r
- */\r
-public class SignTransaction {\r
-\r
-    private final String OP_TXSIGHASH = "ae";\r
-    private final String OP_CHECKMULTISIG  = "ad";\r
-\r
-    public String serializeTransaction(Transaction tx) {\r
-        String txSign = null;\r
-        //开始序列化\r
-        ByteArrayOutputStream stream = new ByteArrayOutputStream();\r
-        try {\r
-            stream.write(7);\r
-            // version\r
-            if (null != tx.version)\r
-                Utils.writeVarint(tx.version, stream);\r
-            if (null != tx.timeRange)\r
-                Utils.writeVarint(tx.timeRange, stream);\r
-            //inputs\r
-            if (null != tx.inputs && tx.inputs.size() > 0) {\r
-                Utils.writeVarint(tx.inputs.size(), stream);\r
-                for (Transaction.AnnotatedInput input : tx.inputs) {\r
-                    if (input.type == 1) {\r
-                        //assertVersion\r
-                        Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
-\r
-                        //inputCommitment\r
-                        ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream();\r
-                        //spend type flag\r
-                        Utils.writeVarint(input.type, inputCommitStream);\r
-                        //spendCommitment\r
-                        ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();\r
-                        spendCommitSteam.write(Hex.decode(input.sourceId)); //计算muxID\r
-                        spendCommitSteam.write(Hex.decode(input.assetId));\r
-                        Utils.writeVarint(input.amount, spendCommitSteam);\r
-                        //sourcePosition\r
-                        Utils.writeVarint(input.sourcePosition, spendCommitSteam); //db中获取position\r
-                        //vmVersion\r
-                        Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version\r
-                        //controlProgram\r
-                        Utils.writeVarStr(Hex.decode(input.controlProgram), spendCommitSteam);\r
-\r
-                        byte[] dataSpendCommit = spendCommitSteam.toByteArray();\r
-\r
-                        Utils.writeVarint(dataSpendCommit.length, inputCommitStream);\r
-                        inputCommitStream.write(dataSpendCommit);\r
-                        byte[] dataInputCommit = inputCommitStream.toByteArray();\r
-                        //inputCommit的length\r
-                        Utils.writeVarint(dataInputCommit.length, stream);\r
-                        stream.write(dataInputCommit);\r
-\r
-                        //inputWitness\r
-                        if (null != input.witnessComponent) {\r
-                            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
-                            //arguments\r
-                            int lenSigs = input.witnessComponent.signatures.length;\r
-                            //arguments的length\r
-                            Utils.writeVarint(lenSigs, witnessStream);\r
-                            for (int i = 0; i < lenSigs; i++) {\r
-                                String sig = input.witnessComponent.signatures[i];\r
-                                Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
-                            }\r
-                            byte[] dataWitnessComponets = witnessStream.toByteArray();\r
-                            //witness的length\r
-                            Utils.writeVarint(dataWitnessComponets.length, stream);\r
-                            stream.write(dataWitnessComponets);\r
-                        }\r
-                    }\r
-                    if (input.type == 0) {\r
-                        //assetVersion\r
-                        Utils.writeVarint(01, stream);\r
-                        //写入 type nonce assetId amount\r
-                        ByteArrayOutputStream issueInfo = new ByteArrayOutputStream();\r
-                        //写入 input.type==00 issue\r
-                        Utils.writeVarint(input.type, issueInfo);\r
-                        //写入 8个字节的随机数\r
-                        Utils.writeVarStr(Hex.decode(input.nonce), issueInfo);\r
-                        issueInfo.write(Hex.decode(input.assetId));\r
-                        Utils.writeVarint(input.amount, issueInfo);\r
-                        stream.write(issueInfo.toByteArray().length);\r
-                        stream.write(issueInfo.toByteArray());\r
-\r
-                        ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream();\r
-                        //未知\r
-                        Utils.writeVarint(1, issueInfo1);\r
-                        //写入assetDefine\r
-                        Utils.writeVarStr(Hex.decode(input.assetDefinition), issueInfo1);\r
-                        //vm.version\r
-                        Utils.writeVarint(1, issueInfo1);\r
-                        //controlProgram\r
-                        Utils.writeVarStr(Hex.decode(input.controlProgram), issueInfo1);\r
-\r
-                        //inputWitness\r
-                        if (null != input.witnessComponent) {\r
-                            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
-                            //arguments\r
-                            int lenSigs = input.witnessComponent.signatures.length;\r
-                            //arguments的length\r
-                            Utils.writeVarint(lenSigs, witnessStream);\r
-                            for (int i = 0; i < lenSigs; i++) {\r
-                                String sig = input.witnessComponent.signatures[i];\r
-                                Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
-                            }\r
-//                            byte[] dataWitnessComponets = witnessStream.toByteArray();\r
-                            //witness的length\r
-//                            Utils.writeVarint(dataWitnessComponets.length, issueInfo1);\r
-\r
-                            issueInfo1.write(witnessStream.toByteArray());\r
-                        }\r
-                        stream.write(issueInfo1.toByteArray().length - 1);\r
-                        stream.write(issueInfo1.toByteArray());\r
-\r
-                    }\r
-                }\r
-            }\r
-\r
-            //outputs\r
-            if (null != tx.outputs && tx.outputs.size() > 0) {\r
-                Utils.writeVarint(tx.outputs.size(), stream);\r
-                for (Transaction.AnnotatedOutput output : tx.outputs) {\r
-                    //assertVersion\r
-                    Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
-                    //outputCommit\r
-                    ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();\r
-                    //assetId\r
-                    outputCommitSteam.write(Hex.decode(output.assetId));\r
-                    //amount\r
-                    Utils.writeVarint(output.amount, outputCommitSteam);\r
-                    //vmVersion\r
-                    Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version\r
-                    //controlProgram\r
-                    Utils.writeVarStr(Hex.decode(output.controlProgram), outputCommitSteam);\r
-\r
-                    byte[] dataOutputCommit = outputCommitSteam.toByteArray();\r
-                    //outputCommit的length\r
-                    Utils.writeVarint(dataOutputCommit.length, stream);\r
-                    stream.write(dataOutputCommit);\r
-\r
-                    //outputWitness\r
-                    Utils.writeVarint(0, stream);\r
-                }\r
-            }\r
-\r
-            byte[] data = stream.toByteArray();\r
-            txSign = Hex.toHexString(data);\r
-\r
-        } catch (IOException e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-        return txSign;\r
-    }\r
-\r
-    public Integer getTransactionSize(Transaction tx) {\r
-        String result = serializeTransaction(tx);\r
-        return Hex.decode(result).length;\r
-    }\r
-\r
-    //签名组装witness\r
-    public Transaction buildWitness(Transaction transaction, int index, byte[] privateKeyBytes) {\r
-\r
-        Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
-        if (null == input.witnessComponent)\r
-\r
-            if (null != input) {\r
-                try {\r
-                    input.witnessComponent = new Transaction.InputWitnessComponent();\r
-                    byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
-                    byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(privateKeyBytes);\r
-                    byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
-\r
-                    switch (input.type) {\r
-                        case 1: {\r
-                            input.witnessComponent.signatures = new String[2];\r
-                            input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
-                            byte[] deriveXpub = DeriveXpub.deriveXpub(privateKeyBytes);\r
-                            String pubKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
-                            input.witnessComponent.signatures[1] = pubKey;\r
-                            break;\r
-                        }\r
-                        case 0: {\r
-                            input.witnessComponent.signatures = new String[1];\r
-                            input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
-                            break;\r
-                        }\r
-                    }\r
-\r
-                } catch (Exception e) {\r
-                    throw new RuntimeException(e);\r
-                }\r
-            } else {\r
-                System.out.println("build witness failed.");\r
-            }\r
-        return transaction;\r
-    }\r
-\r
-    //多签单输入\r
-    public void buildWitness(Transaction transaction, int index, String[] privateKeys) {\r
-        Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
-        if (null == input.witnessComponent)\r
-            input.witnessComponent = new Transaction.InputWitnessComponent();\r
-        try {\r
-\r
-            //TODO 多签issue\r
-            StringBuilder sb = new StringBuilder(OP_TXSIGHASH);\r
-            input.witnessComponent.signatures = new String[privateKeys.length + 1];\r
-            for (int i = 0; i < privateKeys.length; i++) {\r
-                System.out.println(input.isChange());\r
-                byte[] key = DerivePrivateKey.derivePrivateKey(privateKeys[i], 1, input.isChange(), input.getControlProgramIndex());\r
-                byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
-                byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(key);\r
-                byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
-                input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
-                byte[] deriveXpub = DeriveXpub.deriveXpub(expandedPrivateKey);\r
-                String publicKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
-                sb.append(Integer.toString(Hex.decode(publicKey).length,16)).append(publicKey);\r
-            }\r
-            //TODO 签名数跟公钥数量不同\r
-            sb.append(Utils.pushDataInt(privateKeys.length)); //should be nrequired\r
-            sb.append(Utils.pushDataInt(privateKeys.length));\r
-            sb.append(OP_CHECKMULTISIG);\r
-            input.witnessComponent.signatures[privateKeys.length] = sb.toString();\r
-\r
-        } catch (Exception e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-\r
-    }\r
-\r
-    public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) {\r
-\r
-        SHA3.Digest256 digest256 = new SHA3.Digest256();\r
-        // data = hashedInputHex + txID\r
-        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
-        out.write(hashedInputHex, 0, hashedInputHex.length);\r
-        out.write(txID, 0, txID.length);\r
-        byte[] data = out.toByteArray();\r
-        return digest256.digest(data);\r
-    }\r
-\r
-\r
-    public Transaction generateSignatures(Transaction Transaction, BigInteger[] keys) {\r
-        Transaction.AnnotatedInput input = Transaction.inputs.get(0);\r
-        input.witnessComponent.signatures = new String[keys.length];\r
-        for (int i = 0; i < keys.length; i++) {\r
-            if (null != input) {\r
-                try {\r
-                    byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(Transaction.txID));\r
-                    byte[] expandedPrv = Utils.BigIntegerToBytes(keys[i]);\r
-                    byte[] priKey = ExpandedPrivateKey.ExpandedPrivateKey(expandedPrv);\r
-                    byte[] sig = Signer.Ed25519InnerSign(priKey, message);\r
-                    input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
-\r
-                } catch (Exception e) {\r
-                    e.printStackTrace();\r
-                }\r
-            } else {\r
-                System.out.println("generate signatures failed.");\r
-            }\r
-        }\r
-        return Transaction;\r
-    }\r
-\r
-    //根据主私钥对交易签名,生成序列化的rawTransaction\r
-    public String rawTransaction(String rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        byte[] privateChild;\r
-        for (int i = 0; i < tx.inputs.size(); i++) {\r
-            if (tx.inputs.get(i).type == 1) {\r
-                privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, 1, tx.inputs.get(i).isChange(), tx.inputs.get(i).getControlProgramIndex());\r
-            } else {\r
-                privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, tx.inputs.get(i).getKeyIndex());\r
-            }\r
-            buildWitness(tx, i, privateChild);\r
-        }\r
-        return serializeTransaction(tx);\r
-    }\r
-\r
-    //多签\r
-    public String rawTransaction(String[] rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        String privateChild;\r
-        for (int i = 0; i < tx.inputs.size(); i++) {\r
-            if (tx.inputs.get(i).controlProgram.length() != 44) {\r
-                buildWitness(tx, i, rootPrivateKey);\r
-            }\r
-\r
-        }\r
-        return serializeTransaction(tx);\r
-    }\r
-\r
-\r
-}\r
-\r
-\r
index 29167e9..36dd1b2 100755 (executable)
@@ -10,8 +10,7 @@ import java.util.Arrays;
 \r
 public class Signer {\r
 \r
-    public static byte[] Ed25519InnerSign(byte[] privateKey, byte[] message)\r
-            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
+    public static byte[] ed25519InnerSign(byte[] privateKey, byte[] message) throws NoSuchAlgorithmException {\r
         MessageDigest md = MessageDigest.getInstance("SHA-512");\r
         byte[] digestData = new byte[32 + message.length];\r
         int digestDataIndex = 0;\r
diff --git a/tx-signer/src/main/java/io/bytom/api/SpendInput.java b/tx-signer/src/main/java/io/bytom/api/SpendInput.java
new file mode 100644 (file)
index 0000000..7d1fa68
--- /dev/null
@@ -0,0 +1,136 @@
+package io.bytom.api;
+
+import io.bytom.common.*;
+import io.bytom.types.*;
+import org.bouncycastle.util.encoders.Hex;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+public class SpendInput extends BaseInput {
+
+    private String sourceId;
+
+    private long sourcePosition;
+
+    private boolean change;
+
+    private int controlProgramIndex;
+
+    private BIPProtocol bipProtocol;
+
+    public SpendInput() {}
+
+    public SpendInput(String assetID, long amount, String controlProgram) {
+        this.setAssetId(assetID);
+        this.setAmount(amount);
+        this.setProgram(controlProgram);
+        this.bipProtocol = BIPProtocol.BIP44;
+    }
+
+    @Override
+    public InputEntry convertInputEntry(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);
+
+        OutputEntry prevout = new OutputEntry(src, pro, 0);
+        Hash prevOutID = prevout.entryID();
+        entryMap.put(prevOutID, prevout);
+        return new Spend(prevOutID, index);
+    }
+
+    @Override
+    public byte[] serializeInput() throws IOException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        //assetVersion
+        Utils.writeVarint(1, stream); //AssetVersion是否默认为1
+
+        //inputCommitment
+        ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream();
+        //spend type flag
+        Utils.writeVarint(SPEND_INPUT_TYPE, inputCommitStream);
+        //spendCommitment
+        ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();
+        spendCommitSteam.write(Hex.decode(sourceId)); //计算muxID
+        spendCommitSteam.write(Hex.decode(getAssetId()));
+        Utils.writeVarint(getAmount(), spendCommitSteam);
+        //sourcePosition
+        Utils.writeVarint(sourcePosition, spendCommitSteam); //db中获取position
+        //vmVersion
+        Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version
+        //controlProgram
+        Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam);
+
+        byte[] dataSpendCommit = spendCommitSteam.toByteArray();
+
+        Utils.writeVarint(dataSpendCommit.length, inputCommitStream);
+        inputCommitStream.write(dataSpendCommit);
+        byte[] dataInputCommit = inputCommitStream.toByteArray();
+        //inputCommit的length
+        Utils.writeVarint(dataInputCommit.length, stream);
+        stream.write(dataInputCommit);
+
+        //inputWitness
+        if (null != getWitnessComponent()) {
+            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();
+            //arguments
+            int witnessSize = getWitnessComponent().size();
+            //arguments的length
+            Utils.writeVarint(witnessSize, witnessStream);
+            for (int i = 0; i < witnessSize; i++) {
+                String witness = getWitnessComponent().getWitness(i);
+                Utils.writeVarStr(Hex.decode(witness), witnessStream);
+            }
+            byte[] dataWitnessComponents = witnessStream.toByteArray();
+            //witness的length
+            Utils.writeVarint(dataWitnessComponents.length, stream);
+            stream.write(dataWitnessComponents);
+        }
+        return stream.toByteArray();
+    }
+
+    @Override
+    public void buildWitness(String txID) throws Exception {
+        String rootPrvKey = getWitnessComponent().getRootPrivateKey();
+
+        byte[] privateChild;
+        if (bipProtocol == BIPProtocol.BIP44) {
+            privateChild = DerivePrivateKey.bip44derivePrvKey(rootPrvKey, TransactionSigner.AccountKeySpace, change, controlProgramIndex);
+        } else {
+            privateChild = DerivePrivateKey.bip32derivePrvKey(rootPrvKey, getKeyIndex(), TransactionSigner.AccountKeySpace, controlProgramIndex);
+        }
+
+        byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID));
+        byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(privateChild);
+        byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message);
+
+        getWitnessComponent().appendWitness(Hex.toHexString(sig));
+
+        byte[] deriveXpub = DeriveXpub.deriveXpub(privateChild);
+        String pubKey = Hex.toHexString(deriveXpub).substring(0, 64);
+
+        getWitnessComponent().appendWitness(pubKey);
+    }
+
+    public void setSourceId(String sourceId) {
+        this.sourceId = sourceId;
+    }
+
+    public void setSourcePosition(long sourcePosition) {
+        this.sourcePosition = sourcePosition;
+    }
+
+    public void setChange(boolean change) {
+        this.change = change;
+    }
+
+    public void setControlProgramIndex(int controlProgramIndex) {
+        this.controlProgramIndex = controlProgramIndex;
+    }
+
+    public void setBipProtocol(BIPProtocol bipProtocol) {
+        this.bipProtocol = bipProtocol;
+    }
+}
\ No newline at end of file
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
diff --git a/tx-signer/src/main/java/io/bytom/api/TransactionSigner.java b/tx-signer/src/main/java/io/bytom/api/TransactionSigner.java
new file mode 100755 (executable)
index 0000000..5215c2d
--- /dev/null
@@ -0,0 +1,12 @@
+package io.bytom.api;\r
+\r
+/**\r
+ * Created by liqiang on 2018/10/24.\r
+ */\r
+public class TransactionSigner {\r
+\r
+    public static final byte AssetKeySpace = 0;\r
+    public static final byte AccountKeySpace = 1;\r
+}\r
+\r
+\r
index 5fef187..1042073 100755 (executable)
@@ -61,15 +61,15 @@ public class UTXO {
         return Utils.serializer.fromJson(json, UTXO.class);\r
     }\r
 \r
-    public static Transaction.AnnotatedInput utxoToAnnotatedInput(UTXO utxo) {\r
-        Transaction.AnnotatedInput annotatedInput = new Transaction.AnnotatedInput();\r
-        annotatedInput.setAmount(utxo.amount);\r
-        annotatedInput.setControlProgram(utxo.program);\r
-        annotatedInput.setChange(utxo.change);\r
-        annotatedInput.setAssetId(utxo.assetId);\r
-        annotatedInput.setControlProgramIndex(utxo.controlProgramIndex);\r
-        annotatedInput.setSourceId(utxo.sourceId);\r
-        annotatedInput.setSourcePosition(utxo.sourcePos);\r
-        return annotatedInput;\r
+    public SpendInput toSpendAnnotatedInput() {\r
+        SpendInput spendInput = new SpendInput();\r
+        spendInput.setAmount(amount);\r
+        spendInput.setProgram(program);\r
+        spendInput.setChange(change);\r
+        spendInput.setAssetId(assetId);\r
+        spendInput.setControlProgramIndex(controlProgramIndex);\r
+        spendInput.setSourceId(sourceId);\r
+        spendInput.setSourcePosition(sourcePos);\r
+        return spendInput;\r
     }\r
 }\r
diff --git a/tx-signer/src/main/java/io/bytom/api/WitnessComponent.java b/tx-signer/src/main/java/io/bytom/api/WitnessComponent.java
new file mode 100644 (file)
index 0000000..2d25a48
--- /dev/null
@@ -0,0 +1,43 @@
+package io.bytom.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A single witness component, holding information that will become the input
+ * witness.
+ */
+public class WitnessComponent {
+
+    /**
+     * The list of witnesses made with the specified keys (null unless type is
+     * "signature").
+     */
+    private List<String> witnesses;
+
+    private String rootPrivateKey;
+
+    public WitnessComponent() {
+        witnesses = new ArrayList<>();
+    }
+
+    public int size() {
+        return witnesses.size();
+    }
+
+    public String getWitness(int index) {
+        return witnesses.get(index);
+    }
+
+    public void appendWitness(String witness) {
+        witnesses.add(witness);
+    }
+
+    public String getRootPrivateKey() {
+        return rootPrivateKey;
+    }
+
+    public void setRootPrivateKey(String rootPrivateKey) {
+        this.rootPrivateKey = rootPrivateKey;
+    }
+}
\ No newline at end of file
diff --git a/tx-signer/src/main/java/io/bytom/common/Constants.java b/tx-signer/src/main/java/io/bytom/common/Constants.java
deleted file mode 100755 (executable)
index 3ced44d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.bytom.common;\r
-\r
-/**\r
- * Created by liqiang on 2018/10/17.\r
- */\r
-public class Constants {\r
-\r
-    public static int INPUT_TYPE_ISSUANCE = 0;\r
-\r
-    public static int INPUT_TYPE_SPEND = 1;\r
-\r
-    public static int INPUT_TYPE_COINBASE = 2;\r
-}\r
index 9502a41..f7adb6c 100755 (executable)
@@ -10,25 +10,23 @@ import java.security.SignatureException;
 public class DerivePrivateKey {\r
     //bip44 派生子秘钥\r
     //accountIndex 默认为1\r
-    public static byte[] derivePrivateKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        byte[] xprv = Hex.decode(rootPriv);\r
+    public static byte[] bip44derivePrvKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         byte[][] paths = PathUtil.getBip44Path(accountIndex, change, programIndex);\r
-        byte[] res = xprv;\r
+        byte[] res = Hex.decode(rootPriv);\r
         for (int i = 0; i < paths.length; i++) {\r
             byte[] xpub = DeriveXpub.deriveXpub(res);\r
-            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+            res = NonHardenedChild.nhChild(paths[i], res, xpub);\r
         }\r
         return res;\r
     }\r
 \r
     //BIP32 派生子秘钥 issue 需要用到BIP32派生规则\r
-    public static byte[] derivePrivateKey(String rootPrivateKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        byte[] xprv = Hex.decode(rootPrivateKey);\r
-        byte[] res = xprv;\r
-        byte[][] paths = PathUtil.getBip32Path(keyIndex);\r
+    public static byte[] bip32derivePrvKey(String rootPrivateKey, int accountIndex, byte keySpace, long ...programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] res = Hex.decode(rootPrivateKey);\r
+        byte[][] paths = PathUtil.getBip32Path(keySpace, accountIndex, programIndex);\r
         for (int i = 0; i < paths.length; i++) {\r
             byte[] xpub = DeriveXpub.deriveXpub(res);\r
-            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+            res = NonHardenedChild.nhChild(paths[i], res, xpub);\r
         }\r
         return res;\r
     }\r
index 1c214b4..3546df4 100755 (executable)
@@ -6,18 +6,11 @@ public class DeriveXpub {
     public static byte[] deriveXpub(byte[] xprv) {\r
         byte[] xpub = new byte[xprv.length];\r
         byte[] scalar = new byte[xprv.length / 2];\r
-//        for (int i = 0; i < xprv.length / 2; i++) {\r
-//            scalar[i] = xprv[i];\r
-//        }\r
+\r
         System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2);\r
         byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar);\r
-//        for (int i = 0; i < buf.length; i++) {\r
-//            xpub[i] = buf[i];\r
-//        }\r
+\r
         System.arraycopy(buf, 0, xpub, 0, buf.length);\r
-//        for (int i = xprv.length / 2; i < xprv.length; i++) {\r
-//            xpub[i] = xprv[i];\r
-//        }\r
         System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2);\r
         return xpub;\r
     }\r
index 2a23df4..a941efa 100755 (executable)
@@ -9,7 +9,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.SignatureException;\r
 \r
 public class ExpandedPrivateKey {\r
-    public static byte[] HMacSha512(byte[] data, byte[] key)\r
+    public static byte[] hmacSha512(byte[] data, byte[] key)\r
             throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
         SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");\r
         Mac mac = Mac.getInstance("HmacSHA512");\r
@@ -17,10 +17,10 @@ public class ExpandedPrivateKey {
         return mac.doFinal(data);\r
     }\r
 \r
-    public static byte[] ExpandedPrivateKey(byte[] data)\r
+    public static byte[] expandedPrivateKey(byte[] data)\r
             throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
         // "457870616e64" is "Expand" hex.\r
-        byte[] res = HMacSha512(data, Hex.decode("457870616e64"));\r
+        byte[] res = hmacSha512(data, Hex.decode("457870616e64"));\r
         for (int i = 0; i <= 31; i++) {\r
             res[i] = data[i];\r
         }\r
index aebf82f..05b79c5 100755 (executable)
@@ -11,7 +11,7 @@ import java.security.SignatureException;
 \r
 public class NonHardenedChild {\r
 \r
-    private static byte[] HMacSha512(byte[] data, byte[] key)\r
+    private static byte[] hmacSha512(byte[] data, byte[] key)\r
             throws NoSuchAlgorithmException, InvalidKeyException {\r
         SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");\r
         Mac mac = Mac.getInstance("HmacSHA512");\r
@@ -19,7 +19,7 @@ public class NonHardenedChild {
         return mac.doFinal(data);\r
     }\r
 \r
-    public static byte[] NHchild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+    public static byte[] nhChild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         // begin build data\r
         ByteArrayOutputStream out = new ByteArrayOutputStream();\r
         out.write('N');\r
@@ -34,7 +34,7 @@ public class NonHardenedChild {
         // end build key\r
 \r
         // doFinal()\r
-        byte[] res = HMacSha512(data, key);\r
+        byte[] res = hmacSha512(data, key);\r
 \r
         //begin operate res[:32]\r
         byte[] f = new byte[res.length / 2];\r
@@ -76,7 +76,7 @@ public class NonHardenedChild {
         byte[] res = xprv;\r
         for (int i = 0; i < hpaths.length; i++) {\r
             byte[] xpub = DeriveXpub.deriveXpub(res);\r
-            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+            res = NonHardenedChild.nhChild(paths[i], res, xpub);\r
         }\r
         return res;\r
     }\r
index b4bb6f6..0d7eaef 100755 (executable)
@@ -3,6 +3,7 @@ package io.bytom.common;
 import com.google.gson.Gson;\r
 import com.google.gson.GsonBuilder;\r
 import io.bytom.types.ExpandedKeys;\r
+import org.bouncycastle.jcajce.provider.digest.SHA3;\r
 import org.bouncycastle.util.encoders.Hex;\r
 \r
 import java.io.ByteArrayOutputStream;\r
@@ -76,7 +77,7 @@ public class Utils {
     }\r
 \r
     public static ExpandedKeys expandedPriKey(String priKey, String key) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        byte[] hashPriKey = ExpandedPrivateKey.HMacSha512(Hex.decode(priKey), key.getBytes());\r
+        byte[] hashPriKey = ExpandedPrivateKey.hmacSha512(Hex.decode(priKey), key.getBytes());\r
         //begin operate res[:32]\r
         byte[] f = new byte[hashPriKey.length / 2];\r
         System.arraycopy(hashPriKey, 0, f, 0, hashPriKey.length / 2);\r
@@ -101,4 +102,15 @@ public class Utils {
         return null;\r
 \r
     }\r
+\r
+    public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) {\r
+\r
+        SHA3.Digest256 digest256 = new SHA3.Digest256();\r
+        // data = hashedInputHex + txID\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(hashedInputHex, 0, hashedInputHex.length);\r
+        out.write(txID, 0, txID.length);\r
+        byte[] data = out.toByteArray();\r
+        return digest256.digest(data);\r
+    }\r
 }\r
diff --git a/tx-signer/src/main/java/io/bytom/common/VMUtil.java b/tx-signer/src/main/java/io/bytom/common/VMUtil.java
new file mode 100644 (file)
index 0000000..16be8e3
--- /dev/null
@@ -0,0 +1,100 @@
+package io.bytom.common;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class VMUtil {
+
+    private static final byte OP_0 = (byte) 0x00;
+    private static final byte OP_1 = (byte) 0x51;
+    private static final byte OP_PUSHDATA1 = (byte) 0x4c;
+    private static final byte OP_PUSHDATA2 = (byte) 0x4d;
+    private static final byte OP_PUSHDATA4 = (byte) 0x43;
+    private static final byte OP_TXSIGHASH = (byte) 0xae;
+    private static final byte OP_CHECKMULTISIG  = (byte) 0xad;
+
+    public static byte[] p2spMultiSigProgram(byte[][] pubKeys, int nRequired) {
+        checkMultiSigParams(nRequired, pubKeys.length);
+        ByteArrayOutputStream program = new ByteArrayOutputStream();
+        program.write(OP_TXSIGHASH);
+
+        for (byte[] pubKey : pubKeys) {
+            try {
+                program.write(pushDataBytes(pubKey));
+                program.write(pushDataInt64(nRequired));
+                program.write(pushDataInt64(pubKeys.length));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        program.write(OP_CHECKMULTISIG);
+        return program.toByteArray();
+    }
+
+    private static void checkMultiSigParams(int nRequired, int nPubkeys) {
+        if (nRequired < 0 || nPubkeys < 0 || nRequired > nPubkeys || (nRequired == 0 && nPubkeys > 0)) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private static byte[] pushDataBytes(byte[] data) {
+        int len = data.length;
+        if (len == 0) {
+            return new byte[] {OP_0};
+        }
+        if (len <= 75) {
+            byte[] dest = new byte[1 + len];
+            dest[0] = (byte) len;
+            System.arraycopy(data, 0, dest, 1, len);
+            return dest;
+        }
+        if (len < 256) {
+            byte[] dest = new byte[2 + len];
+            dest[0] = OP_PUSHDATA1;
+            dest[1] = (byte) len;
+            System.arraycopy(data, 0, dest, 2, len);
+            return dest;
+        }
+        if (len < 65536) {
+            byte[] dest = new byte[3 + len];
+            dest[0] = OP_PUSHDATA2;
+            dest[1] = (byte) len;
+            dest[2] = (byte) (len >> 8);
+            System.arraycopy(data, 0, dest, 3, len);
+            return dest;
+        }
+        byte[] dest = new byte[5 + len];
+        dest[0] = OP_PUSHDATA4;
+        dest[1] = (byte) len;
+        dest[2] = (byte) (len >> 8);
+        dest[3] = (byte) (len >> 16);
+        dest[4] = (byte) (len >> 24);
+        System.arraycopy(data, 0, dest, 5, len);
+        return dest;
+    }
+
+    private static byte[] pushDataInt64(long n) {
+        if (n == 0) {
+            return new byte[] {OP_0};
+        }
+        if (n >= 1 && n <= 16) {
+            return new byte[] {(byte) (OP_1 + (byte) n - 1)};
+        }
+        return pushDataBytes(int64Bytes(n));
+    }
+
+    private static byte[] int64Bytes(long n) {
+        byte[] bytes = new byte[8];
+        int i = 0;
+        while (n != 0) {
+            bytes[i] = (byte) n;
+            n >>= 8;
+            i++;
+        }
+        byte[] res = new byte[i];
+        System.arraycopy(bytes, 0, res, 0, i);
+        return res;
+    }
+
+}
+
index c1885e0..eb74132 100755 (executable)
@@ -69,7 +69,7 @@ public class Asset {
     }\r
 \r
     private String computeIssueProgram(String rootKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, keyIndex);\r
+        byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, keyIndex, (byte) 0, 1);\r
         byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey);\r
 \r
         String issueProgram = "ae20" + Hex.toHexString(deriveXpub).substring(0, 64) + "5151ad";\r
diff --git a/tx-signer/src/main/java/io/bytom/types/InputEntry.java b/tx-signer/src/main/java/io/bytom/types/InputEntry.java
new file mode 100644 (file)
index 0000000..a743b8f
--- /dev/null
@@ -0,0 +1,10 @@
+package io.bytom.types;
+
+import java.util.Map;
+
+public abstract class InputEntry extends Entry {
+
+    public int ordinal;
+
+    public abstract void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap);
+}
index e292dd4..884c121 100755 (executable)
@@ -1,11 +1,11 @@
 package io.bytom.types;\r
 \r
 import java.io.ByteArrayOutputStream;\r
+import java.util.Map;\r
 \r
-public class Issue extends Entry {\r
+public class Issue extends InputEntry {\r
     public Hash nonceHash;\r
     public AssetAmount assetAmount;\r
-    public int ordinal;\r
     public AssetDefinition assetDefinition;\r
     public ValueDestination witnessDestination;\r
 \r
@@ -15,8 +15,9 @@ public class Issue extends Entry {
         this.ordinal = ordinal;\r
     }\r
 \r
-    public void setDestination(Hash id, AssetAmount val, long pos) {\r
-        this.witnessDestination = new ValueDestination(id, val, pos);\r
+    @Override\r
+    public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) {\r
+        this.witnessDestination = new ValueDestination(id, this.assetAmount, pos);\r
     }\r
 \r
     @Override\r
@@ -2,7 +2,7 @@ package io.bytom.types;
 \r
 import java.io.ByteArrayOutputStream;\r
 \r
-public class Output extends Entry {\r
+public class OutputEntry extends Entry {\r
 \r
     public ValueSource source;\r
 \r
@@ -10,13 +10,13 @@ public class Output extends Entry {
 \r
     public Integer ordinal;\r
 \r
-    public Output() {\r
+    public OutputEntry() {\r
         this.source = new ValueSource();\r
         this.controlProgram = new Program();\r
     }\r
 \r
 \r
-    public Output(ValueSource source, Program controlProgram, Integer ordinal) {\r
+    public OutputEntry(ValueSource source, Program controlProgram, Integer ordinal) {\r
         this.source = source;\r
         this.controlProgram = controlProgram;\r
         this.ordinal = ordinal;\r
index 283eb61..167361b 100755 (executable)
@@ -1,8 +1,9 @@
 package io.bytom.types;\r
 \r
 import java.io.ByteArrayOutputStream;\r
+import java.util.Map;\r
 \r
-public class Spend extends Entry {\r
+public class Spend extends InputEntry {\r
 \r
     public Hash spentOutputID;\r
 \r
@@ -17,8 +18,10 @@ public class Spend extends Entry {
         this.ordinal = ordinal;\r
     }\r
 \r
-    public void setDestination(Hash id, AssetAmount val, long pos) {\r
-        this.witnessDestination = new ValueDestination(id, val, pos);\r
+    @Override\r
+    public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) {\r
+        OutputEntry spendOutput = (OutputEntry) entryMap.get(this.spentOutputID);\r
+        this.witnessDestination = new ValueDestination(id, spendOutput.source.value, pos);\r
     }\r
 \r
     @Override\r
index 5a9b1e7..97cf6f7 100755 (executable)
@@ -32,23 +32,27 @@ public class PathUtil {
         if (change) {\r
             changeStr = "01000000";\r
         }\r
-        byte[][] paths = new byte[][]{\r
+        return new byte[][]{\r
                 Hex.decode("2c000000"),\r
                 Hex.decode("99000000"),\r
                 Hex.decode(accountIndexStr),\r
                 Hex.decode(changeStr),\r
                 Hex.decode(programIndexStr),\r
         };\r
-        return paths;\r
     }\r
 \r
-    public static byte[][] getBip32Path(int accountIndex) {\r
+    public static byte[][] getBip32Path(byte keySpace, long accountIndex, long ...itemIndexes) {\r
         byte[] signerPath = new byte[9];\r
-        byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putInt(accountIndex).array();\r
+        signerPath[0] = keySpace;\r
+        byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(accountIndex).array();\r
         System.arraycopy(path, 0, signerPath, 1, 8);\r
-        byte[][] paths = new byte[][]{\r
-                signerPath\r
-        };\r
-        return paths;\r
+\r
+        byte[][] res = new byte[1 + itemIndexes.length][];\r
+        res[0] = signerPath;\r
+        for (int i = 0; i < itemIndexes.length; i++) {\r
+            path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(itemIndexes[i]).array();\r
+            res[i + 1] = path;\r
+        }\r
+        return res;\r
     }\r
 }\r
index 8dcd960..ccfc2c3 100755 (executable)
 package io.bytom;\r
 \r
 import io.bytom.api.*;\r
-import io.bytom.exception.BytomException;\r
-import io.bytom.http.Client;\r
 import org.bouncycastle.util.encoders.Hex;\r
 import org.junit.Test;\r
 \r
-import java.security.InvalidKeyException;\r
-import java.security.NoSuchAlgorithmException;\r
-import java.security.SignatureException;\r
-\r
 public class AppTest {\r
     String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
     String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
 \r
 \r
     @Test\r
-    public void testSpend() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-        Transaction.AnnotatedInput input = btmUtxoToInput();\r
-        Transaction Transaction = new Transaction.Builder()\r
-//                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
-//                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
-                .addInput(input.setType(1))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(49100000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("0020fa56ca7d47f8528e68e120d0e052885faeb9d090d238fa4266bdde21b137513c"))\r
-                .build(200000);\r
-        io.bytom.api.Transaction transaction = MapTransaction.mapTx(Transaction);\r
-        SignTransaction signTransaction = new SignTransaction();\r
-        String rawTransaction = signTransaction.rawTransaction(rootKey, transaction);\r
-        System.out.println(rawTransaction);\r
+    public void testSpendBIP44() {\r
+        SpendInput input = new SpendInput(btmAssetID, 9800000000L, "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() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
-\r
-        String issueAssetId = "a680606d49daae62ef9cb03263ca82a0b1e3184bb6311ea52a5189207f718789";\r
-        String program = "ae204cae24c2cec15491e70fc554026469496e373df9b9970b23acac8b782da0822d5151ad";\r
-        String assetDefine = "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d";\r
-        Transaction.AnnotatedInput input = btmUtxoToInput();\r
-        Transaction Transaction = new Transaction.Builder()\r
-                .addInput(new Transaction.AnnotatedInput().setType(0).setAssetId(issueAssetId).setControlProgram(program).setAmount(100000000).setAssetDefinition(assetDefine).setChange(false).setKeyIndex(13))\r
-//                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
-//                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
-                .addInput(input.setType(1))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(issueAssetId).setAmount(100000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
-//                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(870000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
-                .build(2000000);\r
-        MapTransaction.mapTx(Transaction);\r
-        SignTransaction sign = new SignTransaction();\r
-        String rawTransaction = sign.rawTransaction(rootKey, Transaction);\r
-        System.out.println(rawTransaction);\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.setAssetDefinition("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() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+    public void testRetire() {\r
         String arbitrary = "77656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c";\r
         String retireControlProgram = "6a"+Integer.toString(Hex.decode(arbitrary).length,16)+arbitrary;\r
         String assetId1 = "207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf";\r
+\r
+        SpendInput input1 = new SpendInput(btmAssetID, 289100000, "0014f1dc52048f439ac7fd74f8106a21da78f00de48f");\r
+        input1.setRootPrivateKey(rootKey);\r
+        input1.setChange(true);\r
+        input1.setControlProgramIndex(41);\r
+        input1.setSourceId("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65");\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.setSourcePosition(1);\r
+\r
+        Output output1 = new Output(btmAssetID, 279100000, "001414d362694eacfa110dc20dec77d610d22340f95b");\r
+        Output output2 = new Output(assetId1, 10000000000L, retireControlProgram);\r
+        Output output3 = new Output(assetId1, 60000000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf");\r
+\r
         Transaction transaction = new Transaction.Builder()\r
-                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(289100000).setControlProgram("0014f1dc52048f439ac7fd74f8106a21da78f00de48f").\r
-                        setChange(true).setControlProgramIndex(41).setSourceId("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65").setSourcePosition(0))\r
-                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(assetId1).setAmount(70000000000l).setControlProgram("0014bb8a039726df1b649738e9973db14a4b4fd4becf").\r
-                        setChange(true).setControlProgramIndex(26).setSourceId("be0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b").setSourcePosition(1))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(279100000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(assetId1).setAmount(10000000000l).setControlProgram(retireControlProgram))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(assetId1).setAmount(60000000000l).setControlProgram("0014bb8a039726df1b649738e9973db14a4b4fd4becf"))\r
-                .build(2000000);\r
-        MapTransaction.mapTx(transaction);\r
-        SignTransaction sign = new SignTransaction();\r
-        String rawTransaction = sign.rawTransaction(rootKey, transaction);\r
-        System.out.println(rawTransaction);\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
 \r
     //utxo\r
-    private Transaction.AnnotatedInput btmUtxoToInput() {\r
+    private SpendInput btmUtxoToInput() {\r
         String utxoJson = "\n" +\r
                 "{\n" +\r
                 "  \"id\": \"687e3c3ca1ee8139e57f43697db6aaeac95b10c75b828ef2fad30abe7d047e6a\",\n" +\r
@@ -97,63 +146,6 @@ public class AppTest {
                 "  \"derive_rule\": 0\n" +\r
                 "}";\r
         UTXO utxo = UTXO.fromJson(utxoJson);\r
-        Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo);\r
-        return input;\r
-    }\r
-\r
-    private Transaction.AnnotatedInput otherAssetUtxoToInput() {\r
-        String utxoJson = "\n" +\r
-                "{\n" +\r
-                "  \"id\": \"a1b729714a9ad74108999b6d2f3628c6eea2bd41d7132fca93f56c3f7c12904e\",\n" +\r
-                "  \"amount\": 89900000000,\n" +\r
-                "  \"address\": \"tm1qstadkjjxp7nn3eegdxc38nvur82z37ek2pq2ul\",\n" +\r
-                "  \"program\": \"001482fadb4a460fa738e72869b113cd9c19d428fb36\",\n" +\r
-                "  \"change\": true,\n" +\r
-                "  \"highest\": 139905,\n" +\r
-                "  \"account_alias\": \"wyjbtm\",\n" +\r
-                "  \"asset_id\": \"207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf\",\n" +\r
-                "  \"asset_alias\": \"TEST3\",\n" +\r
-                "  \"account_id\": \"0NNSS39M00A02\",\n" +\r
-                "  \"control_program_index\": 32,\n" +\r
-                "  \"source_id\": \"2886e635442f2f003e1d211f3264520ffb5239944d718bc834925a0cc0798980\",\n" +\r
-                "  \"source_pos\": 1,\n" +\r
-                "  \"valid_height\": 0,\n" +\r
-                "  \"derive_rule\": 0\n" +\r
-                "}";\r
-        UTXO utxo = UTXO.fromJson(utxoJson);\r
-        Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo);\r
-        return input;\r
-    }\r
-\r
-    // submit rawTransaction\r
-    @Test\r
-    public void SubmitTransaction() throws BytomException {\r
-        Client client = TestUtil.generateClient();\r
-        String raw = "070180897a020160015e0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0a1ed89010001160014f1dc52048f439ac7fd74f8106a21da78f00de48f6302401660121218ab96d9f22cce712541ca34c53f4da40450669854341ca9624ad1cf10d1bfc96449fad5406224afd253ccfbdeab683f7ec7f9ee8f45e47a0c58500f2031ecc1bdd5fb9b40016358340b87646ea39faf55c0c105205cfdfdc6184725f40161015fbe0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80f8cce284020101160014bb8a039726df1b649738e9973db14a4b4fd4becf630240d7b7f1c2ca1048fd6798234f2a1e895762f83e802507a008eff52605611b67390a74eaf228b76f5589ff109b2c20eaa65fad6de2e5ab8a25b54267b607df970b20a71547e1064b5edaad92cdce6b0ace832836ba28fdeaf0b83010bed247fe927c03013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f48a85010116001414d362694eacfa110dc20dec77d610d22340f95b00014b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80c8afa02501246a2277656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c00013e207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80b09dc2df0101160014bb8a039726df1b649738e9973db14a4b4fd4becf00";\r
-        SubmitTransaction.SubmitResponse submitResponse = SubmitTransaction.submitRawTransaction(client, raw);\r
-        System.out.println(submitResponse.tx_id);\r
-    }\r
-\r
-\r
-    //单输入多签\r
-    @Test\r
-    public void testMutiSpend(){\r
-        Transaction.AnnotatedInput input = btmUtxoToInput();\r
-        Transaction Transaction = new Transaction.Builder()\r
-//                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
-//                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
-                .addInput(input.setType(1))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(80000000).setControlProgram("00204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b17813"))\r
-                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
-                .build(200000);\r
-        Transaction transaction = MapTransaction.mapTx(Transaction);\r
-        SignTransaction signTransaction = new SignTransaction();\r
-        String[] rootKeys = new String[3];\r
-        rootKeys[0] = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
-        rootKeys[1] = "50a23bf6200b8a98afc049a7d0296a619e2ee27fa0d6d4d271ca244b280b324347627e543cc079614642c7b88c78ce38092430b01d124663e8b84026aefefde1";\r
-        rootKeys[2] = "00e4bf1251fb5aa37aa2a11dec6c0db5cec3f17aa312dbddb30e06957a32ae503ebcdfd4ad5e29be21ee9ec336e939eb72439cf6d99c785268c8f3d71c1be877";\r
-        signTransaction.buildWitness(transaction, 0, rootKeys);\r
-        String raw = signTransaction.serializeTransaction(transaction);\r
-        System.out.println(raw);\r
+        return utxo.toSpendAnnotatedInput();\r
     }\r
 }\r
index d5dee51..8117852 100755 (executable)
@@ -1,8 +1,5 @@
 package io.bytom.api;\r
 \r
-import io.bytom.common.DerivePrivateKey;\r
-import io.bytom.common.DeriveXpub;\r
-import io.bytom.common.ExpandedPrivateKey;\r
 import org.bouncycastle.util.encoders.Hex;\r
 import org.junit.Test;\r
 \r
@@ -18,7 +15,7 @@ public class SignerTest {
         String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";\r
         String expandedXprv = "20849f14bbe212d1b8917da2e1eda9afc4c21e9dd0a47f1e169a3326d45ae443236f54b987369e86ed78eb2b0a2def89a69ec69ca1059e2efe045796dc583d91";\r
         String hashedMessage = "99ab9ebdba106466371467b036d56a0e54ad2a6035e365a6103ba97ab553fd52";\r
-        byte[] sig = Signer.Ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage));\r
+        byte[] sig = Signer.ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage));\r
         System.out.println("sig:" + Hex.toHexString(sig));\r
         //expected: e628e980c690d9ef4ca8a2edee1654a6b401edc4f1af7bda3ffd97fe412522c3bab671dd4e51d0aeeb64f8d761fbdb03e296ab0c1dcbed4eafa504f412a98100\r
     }\r
index 7834aa9..dce5775 100755 (executable)
@@ -11,7 +11,7 @@ public class DerivePrivateKeyTest {
     @Test\r
     public void testBip44Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
-        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 1, false, 2);\r
+        byte[] derivePrivateKey = DerivePrivateKey.bip44derivePrvKey(rootKey, 1, false, 2);\r
         System.out.println(Hex.toHexString(derivePrivateKey));\r
         //expected 48c65f40d860723e71b03988a22edc9ad00ae0deae992e79fb3b812edb5c3e43e78065bf46d0e8ad922cdae600fd2c2a6239b8f1f504f8f255460c6fcce023ff\r
     }\r
@@ -19,7 +19,7 @@ public class DerivePrivateKeyTest {
     @Test\r
     public void testBip32Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
-        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 2);\r
+        byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 2, (byte) 0);\r
         System.out.println(Hex.toHexString(derivePrivateKey));\r
         //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc\r
     }\r
@@ -27,7 +27,7 @@ public class DerivePrivateKeyTest {
     @Test\r
     public void testBip32PublicKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
-        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 14);\r
+        byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 14, (byte) 0);\r
         byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey);\r
         System.out.println(Hex.toHexString(deriveXpub).substring(0, 64));\r
         //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc\r
index 2e0e4b9..5e45da8 100755 (executable)
@@ -13,7 +13,7 @@ public class ExpandedPrivateKeyTest {
     @Test\r
     public void testExpandedKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
         String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";\r
-        byte[] z = ExpandedPrivateKey.ExpandedPrivateKey(Hex.decode(childXprv));\r
+        byte[] z = ExpandedPrivateKey.expandedPrivateKey(Hex.decode(childXprv));\r
         System.out.println(Hex.toHexString(z));\r
         //expect: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3\r
         //        e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3\r
index 6222cbb..834287c 100755 (executable)
@@ -24,9 +24,8 @@ public class NonHardenedChildTest {
         for (int i = 0; i < hpaths.length; i++) {\r
             byte[] xpub = DeriveXpub.deriveXpub(res);\r
 //            System.out.println("xpub: "+Hex.toHexString(xpub));\r
-            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+            res = NonHardenedChild.nhChild(paths[i], res, xpub);\r
         }\r
-        System.out.println("res: " + Hex.toHexString(res));\r
         //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
         //          e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
     }\r
@@ -36,7 +35,6 @@ public class NonHardenedChildTest {
         String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";\r
         String[] hpaths = {"010400000000000000", "0100000000000000"};\r
         byte[] childXprv = NonHardenedChild.child(Hex.decode(hxprv), hpaths);\r
-        System.out.println("childXprv: " + Hex.toHexString(childXprv));\r
         //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
         //          e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
     }\r