Official Java SDK for Bytom
修订版 | 501037d1bb26d7c896b3a522b4db1e70b9d135d3 (tree) |
---|---|
时间 | 2019-03-20 15:53:39 |
作者 | shenao78 <shenao.78@163....> |
Commiter | shenao78 |
refactor
@@ -76,7 +76,7 @@ Need 3 Parameters: | ||
76 | 76 | |
77 | 77 | ```java |
78 | 78 | |
79 | -//utxo json to input public Transaction.AnnotatedInput btmUtxoToInput() { | |
79 | +AnnotatedBaseInput | |
80 | 80 | String utxoJson = "{\n" + |
81 | 81 | " \"id\": \"cf2f5c7340490d33d535a680dc8d95bb66fcccbf1045706484621cc067b982ae\",\n" + |
82 | 82 | " \"amount\": 70000000,\n" + |
@@ -889,7 +889,7 @@ public final class Ed25519 { | ||
889 | 889 | * Input: |
890 | 890 | * s[0]+256*s[1]+...+256^63*s[63] = s |
891 | 891 | * <p> |
892 | - * Output: | |
892 | + * OutputEntry: | |
893 | 893 | * s[0]+256*s[1]+...+256^31*s[31] = s mod l |
894 | 894 | * where l = 2^252 + 27742317777372353535851937790883648493. |
895 | 895 | * Overwrites s in place. |
@@ -1250,7 +1250,7 @@ public final class Ed25519 { | ||
1250 | 1250 | * b[0]+256*b[1]+...+256^31*b[31] = b |
1251 | 1251 | * c[0]+256*c[1]+...+256^31*c[31] = c |
1252 | 1252 | * <p> |
1253 | - * Output: | |
1253 | + * OutputEntry: | |
1254 | 1254 | * s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l |
1255 | 1255 | * where l = 2^252 + 27742317777372353535851937790883648493. |
1256 | 1256 | */ |
@@ -0,0 +1,7 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +public enum BIPProtocol { | |
4 | + | |
5 | + BIP32, | |
6 | + BIP44 | |
7 | +} |
@@ -0,0 +1,107 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +import io.bytom.common.Utils; | |
4 | +import io.bytom.types.*; | |
5 | +import java.io.IOException; | |
6 | +import java.util.Map; | |
7 | + | |
8 | +public abstract class BaseInput { | |
9 | + | |
10 | + public static final int ISSUANCE_INPUT_TYPE = 0; | |
11 | + public static final int SPEND_INPUT_TYPE = 1; | |
12 | + | |
13 | + private String inputID; | |
14 | + | |
15 | + /** | |
16 | + * The number of units of the asset being issued or spent. | |
17 | + */ | |
18 | + private long amount; | |
19 | + | |
20 | + /** | |
21 | + * The id of the asset being issued or spent. | |
22 | + */ | |
23 | + private String assetId; | |
24 | + | |
25 | + /** | |
26 | + * The program which must be satisfied to transfer this output. | |
27 | + */ | |
28 | + private String program; | |
29 | + | |
30 | + private int vmVersion; | |
31 | + | |
32 | + private int keyIndex; | |
33 | + | |
34 | + private WitnessComponent witnessComponent = new WitnessComponent(); | |
35 | + | |
36 | + public BaseInput() { | |
37 | + this.vmVersion = 1; | |
38 | + } | |
39 | + | |
40 | + public abstract InputEntry convertInputEntry(Map<Hash, Entry> entryMap, int index); | |
41 | + | |
42 | + public abstract byte[] serializeInput() throws IOException; | |
43 | + | |
44 | + public abstract void buildWitness(String txID) throws Exception; | |
45 | + | |
46 | + @Override | |
47 | + public String toString() { | |
48 | + return Utils.serializer.toJson(this); | |
49 | + } | |
50 | + | |
51 | + | |
52 | + public AssetAmount getAssetAmount() { | |
53 | + return new AssetAmount(new AssetID(this.assetId), this.amount); | |
54 | + } | |
55 | + | |
56 | + public void setRootPrivateKey(String prvKey) { | |
57 | + witnessComponent.setRootPrivateKey(prvKey); | |
58 | + } | |
59 | + | |
60 | + public String getInputID() { | |
61 | + return inputID; | |
62 | + } | |
63 | + | |
64 | + public int getKeyIndex() { | |
65 | + return keyIndex; | |
66 | + } | |
67 | + | |
68 | + public int getVmVersion() { | |
69 | + return vmVersion; | |
70 | + } | |
71 | + | |
72 | + public String getProgram() { | |
73 | + return program; | |
74 | + } | |
75 | + | |
76 | + public String getAssetId() { | |
77 | + return assetId; | |
78 | + } | |
79 | + | |
80 | + public long getAmount() { | |
81 | + return amount; | |
82 | + } | |
83 | + | |
84 | + public void setInputID(String inputID) { | |
85 | + this.inputID = inputID; | |
86 | + } | |
87 | + | |
88 | + public void setAmount(long amount) { | |
89 | + this.amount = amount; | |
90 | + } | |
91 | + | |
92 | + public void setAssetId(String assetId) { | |
93 | + this.assetId = assetId; | |
94 | + } | |
95 | + | |
96 | + public void setProgram(String program) { | |
97 | + this.program = program; | |
98 | + } | |
99 | + | |
100 | + public void setKeyIndex(int keyIndex) { | |
101 | + this.keyIndex = keyIndex; | |
102 | + } | |
103 | + | |
104 | + public WitnessComponent getWitnessComponent() { | |
105 | + return witnessComponent; | |
106 | + } | |
107 | +} |
@@ -0,0 +1,104 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +import io.bytom.common.DerivePrivateKey; | |
4 | +import io.bytom.common.ExpandedPrivateKey; | |
5 | +import io.bytom.common.Utils; | |
6 | +import io.bytom.types.*; | |
7 | +import io.bytom.util.SHA3Util; | |
8 | +import org.bouncycastle.util.encoders.Hex; | |
9 | +import java.io.ByteArrayOutputStream; | |
10 | +import java.io.IOException; | |
11 | +import java.security.SecureRandom; | |
12 | +import java.util.Map; | |
13 | + | |
14 | +public class IssuanceInput extends BaseInput { | |
15 | + | |
16 | + private String nonce; | |
17 | + | |
18 | + private String assetDefinition; | |
19 | + | |
20 | + public IssuanceInput() {} | |
21 | + | |
22 | + @Override | |
23 | + public InputEntry convertInputEntry(Map<Hash, Entry> entryMap, int index) { | |
24 | + if (this.nonce == null) { | |
25 | + SecureRandom sr = new SecureRandom(); | |
26 | + byte[] randBytes = new byte[8]; | |
27 | + sr.nextBytes(randBytes); | |
28 | + this.nonce = Hex.toHexString(randBytes); | |
29 | + } | |
30 | + | |
31 | + Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(this.nonce))); | |
32 | + Hash assetDefHash = new Hash(this.assetDefinition); | |
33 | + AssetAmount value = this.getAssetAmount(); | |
34 | + | |
35 | + Issue issuance = new Issue(nonceHash, value, index); | |
36 | + Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram())); | |
37 | + issuance.assetDefinition = new AssetDefinition(assetDefHash, pro); | |
38 | + return issuance; | |
39 | + } | |
40 | + | |
41 | + @Override | |
42 | + public byte[] serializeInput() throws IOException { | |
43 | + ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
44 | + //assetVersion | |
45 | + Utils.writeVarint(1, stream); | |
46 | + //写入 type nonce assetId amount | |
47 | + ByteArrayOutputStream issueInfo = new ByteArrayOutputStream(); | |
48 | + //写入 input.type==00 issue | |
49 | + Utils.writeVarint(ISSUANCE_INPUT_TYPE, issueInfo); | |
50 | + //写入 8个字节的随机数 | |
51 | + Utils.writeVarStr(Hex.decode(nonce), issueInfo); | |
52 | + issueInfo.write(Hex.decode(getAssetId())); | |
53 | + Utils.writeVarint(getAmount(), issueInfo); | |
54 | + stream.write(issueInfo.toByteArray().length); | |
55 | + stream.write(issueInfo.toByteArray()); | |
56 | + | |
57 | + ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream(); | |
58 | + //未知 | |
59 | + Utils.writeVarint(1, issueInfo1); | |
60 | + //写入assetDefine | |
61 | + Utils.writeVarStr(Hex.decode(assetDefinition), issueInfo1); | |
62 | + //vm.version | |
63 | + Utils.writeVarint(1, issueInfo1); | |
64 | + //controlProgram | |
65 | + Utils.writeVarStr(Hex.decode(getProgram()), issueInfo1); | |
66 | + | |
67 | + //inputWitness | |
68 | + if (null != getWitnessComponent()) { | |
69 | + ByteArrayOutputStream witnessStream = new ByteArrayOutputStream(); | |
70 | + //arguments | |
71 | + int witnessSize = getWitnessComponent().size(); | |
72 | + //arguments的length | |
73 | + Utils.writeVarint(witnessSize, witnessStream); | |
74 | + for (int i = 0; i < witnessSize; i++) { | |
75 | + String witness = getWitnessComponent().getWitness(i); | |
76 | + Utils.writeVarStr(Hex.decode(witness), witnessStream); | |
77 | + } | |
78 | + issueInfo1.write(witnessStream.toByteArray()); | |
79 | + } | |
80 | + stream.write(issueInfo1.toByteArray().length - 1); | |
81 | + stream.write(issueInfo1.toByteArray()); | |
82 | + return stream.toByteArray(); | |
83 | + } | |
84 | + | |
85 | + @Override | |
86 | + public void buildWitness(String txID) throws Exception { | |
87 | + String rootPrivateKey = getWitnessComponent().getRootPrivateKey(); | |
88 | + byte[] childPrivateKey = DerivePrivateKey.bip32derivePrvKey(rootPrivateKey, getKeyIndex(), TransactionSigner.AssetKeySpace); | |
89 | + | |
90 | + byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID)); | |
91 | + byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(childPrivateKey); | |
92 | + byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message); | |
93 | + | |
94 | + getWitnessComponent().appendWitness(Hex.toHexString(sig)); | |
95 | + } | |
96 | + | |
97 | + public void setNonce(String nonce) { | |
98 | + this.nonce = nonce; | |
99 | + } | |
100 | + | |
101 | + public void setAssetDefinition(String assetDefinition) { | |
102 | + this.assetDefinition = assetDefinition; | |
103 | + } | |
104 | +} |
@@ -1,110 +0,0 @@ | ||
1 | -package io.bytom.api; | |
2 | - | |
3 | -import io.bytom.types.*; | |
4 | -import io.bytom.util.SHA3Util; | |
5 | -import org.bouncycastle.util.encoders.Hex; | |
6 | - | |
7 | -import java.security.SecureRandom; | |
8 | -import java.util.ArrayList; | |
9 | -import java.util.HashMap; | |
10 | -import java.util.List; | |
11 | -import java.util.Map; | |
12 | - | |
13 | -public class MapTransaction { | |
14 | - | |
15 | - public static Transaction mapTx(Transaction tx) { | |
16 | - Map<Hash, Entry> entryMap = new HashMap<>(); | |
17 | - ValueSource[] muxSources = new ValueSource[tx.inputs.size()]; | |
18 | - List<Spend> spends = new ArrayList<>(); | |
19 | - List<Issue> issuances = new ArrayList<>(); | |
20 | - try { | |
21 | - for (int i = 0; i < tx.inputs.size(); i++) { | |
22 | - Transaction.AnnotatedInput input = tx.inputs.get(i); | |
23 | - switch (input.type) { | |
24 | - case 1: { | |
25 | - Program pro = new Program(input.type, Hex.decode(input.controlProgram)); | |
26 | - AssetID assetID = new AssetID(input.assetId); | |
27 | - AssetAmount assetAmount = new AssetAmount(assetID, input.amount); | |
28 | - Hash sourceID = new Hash(input.sourceId); | |
29 | - ValueSource src = new ValueSource(sourceID, assetAmount, input.sourcePosition); | |
30 | - | |
31 | - Output prevout = new Output(src, pro, 0); | |
32 | - Hash prevoutID = addEntry(entryMap, prevout); | |
33 | - Spend spend = new Spend(prevoutID, i); | |
34 | - Hash spendID = addEntry(entryMap, spend); | |
35 | - input.inputID = spendID.toString(); | |
36 | - | |
37 | - muxSources[i] = new ValueSource(spendID, assetAmount, 0); | |
38 | - spends.add(spend); | |
39 | - break; | |
40 | - } | |
41 | - case 0: { | |
42 | - if (input.nonce == null) { | |
43 | - SecureRandom sr = new SecureRandom(); | |
44 | - byte[] randBytes = new byte[8]; | |
45 | - sr.nextBytes(randBytes); | |
46 | - input.nonce = Hex.toHexString(randBytes); | |
47 | - } | |
48 | - Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(input.nonce))); | |
49 | - Hash assetDefHash = new Hash(input.assetDefinition); | |
50 | - AssetID assetID = new AssetID(input.assetId); | |
51 | - AssetAmount value = new AssetAmount(assetID, input.amount); | |
52 | - | |
53 | - Issue issuance = new Issue(nonceHash, value, i); | |
54 | - Program pro = new Program(input.type, Hex.decode(input.controlProgram)); | |
55 | - issuance.assetDefinition = new AssetDefinition(assetDefHash, pro); | |
56 | - Hash issuanceID = addEntry(entryMap, issuance); | |
57 | - input.inputID = issuanceID.toString(); | |
58 | - muxSources[i] = new ValueSource(issuanceID, value, 0); | |
59 | - issuances.add(issuance); | |
60 | - break; | |
61 | - } | |
62 | - } | |
63 | - } | |
64 | - Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51})); | |
65 | - Hash muxID = addEntry(entryMap, mux); | |
66 | - for (Spend spend : spends) { | |
67 | - Output spendOutput = (Output) entryMap.get(spend.spentOutputID); | |
68 | - spend.setDestination(muxID, spendOutput.source.value, spend.ordinal); | |
69 | - } | |
70 | - for (Issue issue : issuances) { | |
71 | - issue.setDestination(muxID, issue.assetAmount, issue.ordinal); | |
72 | - } | |
73 | - | |
74 | - List<Hash> resultIDList = new ArrayList<>(); | |
75 | - for (int i = 0; i < tx.outputs.size(); i++) { | |
76 | - Transaction.AnnotatedOutput output = tx.outputs.get(i); | |
77 | - | |
78 | - AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount); | |
79 | - ValueSource src = new ValueSource(muxID, amount, i); | |
80 | - Hash resultID; | |
81 | - if (output.controlProgram.startsWith("6a")&&output.controlProgram.length()>0) { | |
82 | - Retirement retirement = new Retirement(src, i); | |
83 | - resultID = addEntry(entryMap, retirement); | |
84 | - } else { | |
85 | - Program prog = new Program(1, Hex.decode(output.controlProgram)); | |
86 | - Output oup = new Output(src, prog, i); | |
87 | - resultID = addEntry(entryMap, oup); | |
88 | - } | |
89 | - resultIDList.add(resultID); | |
90 | - output.id = resultID.toString(); | |
91 | - | |
92 | - ValueDestination destination = new ValueDestination(resultID, src.value, 0); | |
93 | - mux.witnessDestinations.add(destination); | |
94 | - } | |
95 | - | |
96 | - TxHeader txHeader = new TxHeader(tx.version, tx.size, tx.timeRange, resultIDList.toArray(new Hash[]{})); | |
97 | - Hash txID = addEntry(entryMap, txHeader); | |
98 | - tx.txID = txID.toString(); | |
99 | - } catch (Exception e) { | |
100 | - throw new RuntimeException(e); | |
101 | - } | |
102 | - return tx; | |
103 | - } | |
104 | - | |
105 | - private static Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) { | |
106 | - Hash id = entry.entryID(); | |
107 | - entryMap.put(id, entry); | |
108 | - return id; | |
109 | - } | |
110 | -} |
@@ -0,0 +1,108 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +import io.bytom.common.Utils; | |
4 | +import org.bouncycastle.util.encoders.Hex; | |
5 | +import java.io.ByteArrayOutputStream; | |
6 | +import java.io.IOException; | |
7 | +import java.util.Map; | |
8 | + | |
9 | +public class Output { | |
10 | + | |
11 | + /** | |
12 | + * address | |
13 | + */ | |
14 | + public String address; | |
15 | + | |
16 | + /** | |
17 | + * The number of units of the asset being controlled. | |
18 | + */ | |
19 | + public long amount; | |
20 | + | |
21 | + /** | |
22 | + * The definition of the asset being controlled (possibly null). | |
23 | + */ | |
24 | + public Map<String, Object> assetDefinition; | |
25 | + | |
26 | + /** | |
27 | + * The id of the asset being controlled. | |
28 | + */ | |
29 | + public String assetId; | |
30 | + | |
31 | + /** | |
32 | + * The control program which must be satisfied to transfer this output. | |
33 | + */ | |
34 | + public String controlProgram; | |
35 | + | |
36 | + /** | |
37 | + * The id of the output. | |
38 | + */ | |
39 | + public String id; | |
40 | + | |
41 | + /** | |
42 | + * The output's position in a transaction's list of outputs. | |
43 | + */ | |
44 | + public Integer position; | |
45 | + | |
46 | + public Output(String assetId, long amount, String controlProgram) { | |
47 | + this.assetId = assetId; | |
48 | + this.amount = amount; | |
49 | + this.controlProgram = controlProgram; | |
50 | + } | |
51 | + | |
52 | + public byte[] serializeOutput() throws IOException { | |
53 | + ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
54 | + | |
55 | + //assetVersion | |
56 | + Utils.writeVarint(1, stream); //AssetVersion是否默认为1 | |
57 | + //outputCommit | |
58 | + ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream(); | |
59 | + //assetId | |
60 | + outputCommitSteam.write(Hex.decode(assetId)); | |
61 | + //amount | |
62 | + Utils.writeVarint(amount, outputCommitSteam); | |
63 | + //vmVersion | |
64 | + Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version | |
65 | + //controlProgram | |
66 | + Utils.writeVarStr(Hex.decode(controlProgram), outputCommitSteam); | |
67 | + | |
68 | + byte[] dataOutputCommit = outputCommitSteam.toByteArray(); | |
69 | + //outputCommit的length | |
70 | + Utils.writeVarint(dataOutputCommit.length, stream); | |
71 | + stream.write(dataOutputCommit); | |
72 | + | |
73 | + //outputWitness | |
74 | + Utils.writeVarint(0, stream); | |
75 | + return stream.toByteArray(); | |
76 | + } | |
77 | + | |
78 | + /** | |
79 | + * The type the output.<br> | |
80 | + * Possible values are "control" and "retire". | |
81 | + */ | |
82 | + public String type; | |
83 | + | |
84 | + public Output setAddress(String address) { | |
85 | + this.address = address; | |
86 | + return this; | |
87 | + } | |
88 | + | |
89 | + public Output setAmount(long amount) { | |
90 | + this.amount = amount; | |
91 | + return this; | |
92 | + } | |
93 | + | |
94 | + public Output setAssetId(String assetId) { | |
95 | + this.assetId = assetId; | |
96 | + return this; | |
97 | + } | |
98 | + | |
99 | + public Output setControlProgram(String controlProgram) { | |
100 | + this.controlProgram = controlProgram; | |
101 | + return this; | |
102 | + } | |
103 | + | |
104 | + public Output setPosition(Integer position) { | |
105 | + this.position = position; | |
106 | + return this; | |
107 | + } | |
108 | +} |
@@ -1,310 +0,0 @@ | ||
1 | -package io.bytom.api; | |
2 | - | |
3 | -import io.bytom.common.DerivePrivateKey; | |
4 | -import io.bytom.common.DeriveXpub; | |
5 | -import io.bytom.common.ExpandedPrivateKey; | |
6 | -import io.bytom.common.Utils; | |
7 | -import org.bouncycastle.jcajce.provider.digest.SHA3; | |
8 | -import org.bouncycastle.util.encoders.Hex; | |
9 | - | |
10 | -import java.io.ByteArrayOutputStream; | |
11 | -import java.io.IOException; | |
12 | -import java.math.BigInteger; | |
13 | -import java.security.InvalidKeyException; | |
14 | -import java.security.NoSuchAlgorithmException; | |
15 | -import java.security.SignatureException; | |
16 | - | |
17 | - | |
18 | -/** | |
19 | - * Created by liqiang on 2018/10/24. | |
20 | - */ | |
21 | -public class SignTransaction { | |
22 | - | |
23 | - private final String OP_TXSIGHASH = "ae"; | |
24 | - private final String OP_CHECKMULTISIG = "ad"; | |
25 | - | |
26 | - public String serializeTransaction(Transaction tx) { | |
27 | - String txSign = null; | |
28 | - //开始序列化 | |
29 | - ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
30 | - try { | |
31 | - stream.write(7); | |
32 | - // version | |
33 | - if (null != tx.version) | |
34 | - Utils.writeVarint(tx.version, stream); | |
35 | - if (null != tx.timeRange) | |
36 | - Utils.writeVarint(tx.timeRange, stream); | |
37 | - //inputs | |
38 | - if (null != tx.inputs && tx.inputs.size() > 0) { | |
39 | - Utils.writeVarint(tx.inputs.size(), stream); | |
40 | - for (Transaction.AnnotatedInput input : tx.inputs) { | |
41 | - if (input.type == 1) { | |
42 | - //assertVersion | |
43 | - Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1 | |
44 | - | |
45 | - //inputCommitment | |
46 | - ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream(); | |
47 | - //spend type flag | |
48 | - Utils.writeVarint(input.type, inputCommitStream); | |
49 | - //spendCommitment | |
50 | - ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream(); | |
51 | - spendCommitSteam.write(Hex.decode(input.sourceId)); //计算muxID | |
52 | - spendCommitSteam.write(Hex.decode(input.assetId)); | |
53 | - Utils.writeVarint(input.amount, spendCommitSteam); | |
54 | - //sourcePosition | |
55 | - Utils.writeVarint(input.sourcePosition, spendCommitSteam); //db中获取position | |
56 | - //vmVersion | |
57 | - Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version | |
58 | - //controlProgram | |
59 | - Utils.writeVarStr(Hex.decode(input.controlProgram), spendCommitSteam); | |
60 | - | |
61 | - byte[] dataSpendCommit = spendCommitSteam.toByteArray(); | |
62 | - | |
63 | - Utils.writeVarint(dataSpendCommit.length, inputCommitStream); | |
64 | - inputCommitStream.write(dataSpendCommit); | |
65 | - byte[] dataInputCommit = inputCommitStream.toByteArray(); | |
66 | - //inputCommit的length | |
67 | - Utils.writeVarint(dataInputCommit.length, stream); | |
68 | - stream.write(dataInputCommit); | |
69 | - | |
70 | - //inputWitness | |
71 | - if (null != input.witnessComponent) { | |
72 | - ByteArrayOutputStream witnessStream = new ByteArrayOutputStream(); | |
73 | - //arguments | |
74 | - int lenSigs = input.witnessComponent.signatures.length; | |
75 | - //arguments的length | |
76 | - Utils.writeVarint(lenSigs, witnessStream); | |
77 | - for (int i = 0; i < lenSigs; i++) { | |
78 | - String sig = input.witnessComponent.signatures[i]; | |
79 | - Utils.writeVarStr(Hex.decode(sig), witnessStream); | |
80 | - } | |
81 | - byte[] dataWitnessComponets = witnessStream.toByteArray(); | |
82 | - //witness的length | |
83 | - Utils.writeVarint(dataWitnessComponets.length, stream); | |
84 | - stream.write(dataWitnessComponets); | |
85 | - } | |
86 | - } | |
87 | - if (input.type == 0) { | |
88 | - //assetVersion | |
89 | - Utils.writeVarint(01, stream); | |
90 | - //写入 type nonce assetId amount | |
91 | - ByteArrayOutputStream issueInfo = new ByteArrayOutputStream(); | |
92 | - //写入 input.type==00 issue | |
93 | - Utils.writeVarint(input.type, issueInfo); | |
94 | - //写入 8个字节的随机数 | |
95 | - Utils.writeVarStr(Hex.decode(input.nonce), issueInfo); | |
96 | - issueInfo.write(Hex.decode(input.assetId)); | |
97 | - Utils.writeVarint(input.amount, issueInfo); | |
98 | - stream.write(issueInfo.toByteArray().length); | |
99 | - stream.write(issueInfo.toByteArray()); | |
100 | - | |
101 | - ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream(); | |
102 | - //未知 | |
103 | - Utils.writeVarint(1, issueInfo1); | |
104 | - //写入assetDefine | |
105 | - Utils.writeVarStr(Hex.decode(input.assetDefinition), issueInfo1); | |
106 | - //vm.version | |
107 | - Utils.writeVarint(1, issueInfo1); | |
108 | - //controlProgram | |
109 | - Utils.writeVarStr(Hex.decode(input.controlProgram), issueInfo1); | |
110 | - | |
111 | - //inputWitness | |
112 | - if (null != input.witnessComponent) { | |
113 | - ByteArrayOutputStream witnessStream = new ByteArrayOutputStream(); | |
114 | - //arguments | |
115 | - int lenSigs = input.witnessComponent.signatures.length; | |
116 | - //arguments的length | |
117 | - Utils.writeVarint(lenSigs, witnessStream); | |
118 | - for (int i = 0; i < lenSigs; i++) { | |
119 | - String sig = input.witnessComponent.signatures[i]; | |
120 | - Utils.writeVarStr(Hex.decode(sig), witnessStream); | |
121 | - } | |
122 | -// byte[] dataWitnessComponets = witnessStream.toByteArray(); | |
123 | - //witness的length | |
124 | -// Utils.writeVarint(dataWitnessComponets.length, issueInfo1); | |
125 | - | |
126 | - issueInfo1.write(witnessStream.toByteArray()); | |
127 | - } | |
128 | - stream.write(issueInfo1.toByteArray().length - 1); | |
129 | - stream.write(issueInfo1.toByteArray()); | |
130 | - | |
131 | - } | |
132 | - } | |
133 | - } | |
134 | - | |
135 | - //outputs | |
136 | - if (null != tx.outputs && tx.outputs.size() > 0) { | |
137 | - Utils.writeVarint(tx.outputs.size(), stream); | |
138 | - for (Transaction.AnnotatedOutput output : tx.outputs) { | |
139 | - //assertVersion | |
140 | - Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1 | |
141 | - //outputCommit | |
142 | - ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream(); | |
143 | - //assetId | |
144 | - outputCommitSteam.write(Hex.decode(output.assetId)); | |
145 | - //amount | |
146 | - Utils.writeVarint(output.amount, outputCommitSteam); | |
147 | - //vmVersion | |
148 | - Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version | |
149 | - //controlProgram | |
150 | - Utils.writeVarStr(Hex.decode(output.controlProgram), outputCommitSteam); | |
151 | - | |
152 | - byte[] dataOutputCommit = outputCommitSteam.toByteArray(); | |
153 | - //outputCommit的length | |
154 | - Utils.writeVarint(dataOutputCommit.length, stream); | |
155 | - stream.write(dataOutputCommit); | |
156 | - | |
157 | - //outputWitness | |
158 | - Utils.writeVarint(0, stream); | |
159 | - } | |
160 | - } | |
161 | - | |
162 | - byte[] data = stream.toByteArray(); | |
163 | - txSign = Hex.toHexString(data); | |
164 | - | |
165 | - } catch (IOException e) { | |
166 | - throw new RuntimeException(e); | |
167 | - } | |
168 | - return txSign; | |
169 | - } | |
170 | - | |
171 | - public Integer getTransactionSize(Transaction tx) { | |
172 | - String result = serializeTransaction(tx); | |
173 | - return Hex.decode(result).length; | |
174 | - } | |
175 | - | |
176 | - //签名组装witness | |
177 | - public Transaction buildWitness(Transaction transaction, int index, byte[] privateKeyBytes) { | |
178 | - | |
179 | - Transaction.AnnotatedInput input = transaction.inputs.get(index); | |
180 | - if (null == input.witnessComponent) | |
181 | - | |
182 | - if (null != input) { | |
183 | - try { | |
184 | - input.witnessComponent = new Transaction.InputWitnessComponent(); | |
185 | - byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID)); | |
186 | - byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(privateKeyBytes); | |
187 | - byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message); | |
188 | - | |
189 | - switch (input.type) { | |
190 | - case 1: { | |
191 | - input.witnessComponent.signatures = new String[2]; | |
192 | - input.witnessComponent.signatures[0] = Hex.toHexString(sig); | |
193 | - byte[] deriveXpub = DeriveXpub.deriveXpub(privateKeyBytes); | |
194 | - String pubKey = Hex.toHexString(deriveXpub).substring(0, 64); | |
195 | - input.witnessComponent.signatures[1] = pubKey; | |
196 | - break; | |
197 | - } | |
198 | - case 0: { | |
199 | - input.witnessComponent.signatures = new String[1]; | |
200 | - input.witnessComponent.signatures[0] = Hex.toHexString(sig); | |
201 | - break; | |
202 | - } | |
203 | - } | |
204 | - | |
205 | - } catch (Exception e) { | |
206 | - throw new RuntimeException(e); | |
207 | - } | |
208 | - } else { | |
209 | - System.out.println("build witness failed."); | |
210 | - } | |
211 | - return transaction; | |
212 | - } | |
213 | - | |
214 | - //多签单输入 | |
215 | - public void buildWitness(Transaction transaction, int index, String[] privateKeys) { | |
216 | - Transaction.AnnotatedInput input = transaction.inputs.get(index); | |
217 | - if (null == input.witnessComponent) | |
218 | - input.witnessComponent = new Transaction.InputWitnessComponent(); | |
219 | - try { | |
220 | - | |
221 | - //TODO 多签issue | |
222 | - StringBuilder sb = new StringBuilder(OP_TXSIGHASH); | |
223 | - input.witnessComponent.signatures = new String[privateKeys.length + 1]; | |
224 | - for (int i = 0; i < privateKeys.length; i++) { | |
225 | - System.out.println(input.isChange()); | |
226 | - byte[] key = DerivePrivateKey.derivePrivateKey(privateKeys[i], 1, input.isChange(), input.getControlProgramIndex()); | |
227 | - byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID)); | |
228 | - byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(key); | |
229 | - byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message); | |
230 | - input.witnessComponent.signatures[i] = Hex.toHexString(sig); | |
231 | - byte[] deriveXpub = DeriveXpub.deriveXpub(expandedPrivateKey); | |
232 | - String publicKey = Hex.toHexString(deriveXpub).substring(0, 64); | |
233 | - sb.append(Integer.toString(Hex.decode(publicKey).length,16)).append(publicKey); | |
234 | - } | |
235 | - //TODO 签名数跟公钥数量不同 | |
236 | - sb.append(Utils.pushDataInt(privateKeys.length)); //should be nrequired | |
237 | - sb.append(Utils.pushDataInt(privateKeys.length)); | |
238 | - sb.append(OP_CHECKMULTISIG); | |
239 | - input.witnessComponent.signatures[privateKeys.length] = sb.toString(); | |
240 | - | |
241 | - } catch (Exception e) { | |
242 | - throw new RuntimeException(e); | |
243 | - } | |
244 | - | |
245 | - } | |
246 | - | |
247 | - public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) { | |
248 | - | |
249 | - SHA3.Digest256 digest256 = new SHA3.Digest256(); | |
250 | - // data = hashedInputHex + txID | |
251 | - ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
252 | - out.write(hashedInputHex, 0, hashedInputHex.length); | |
253 | - out.write(txID, 0, txID.length); | |
254 | - byte[] data = out.toByteArray(); | |
255 | - return digest256.digest(data); | |
256 | - } | |
257 | - | |
258 | - | |
259 | - public Transaction generateSignatures(Transaction Transaction, BigInteger[] keys) { | |
260 | - Transaction.AnnotatedInput input = Transaction.inputs.get(0); | |
261 | - input.witnessComponent.signatures = new String[keys.length]; | |
262 | - for (int i = 0; i < keys.length; i++) { | |
263 | - if (null != input) { | |
264 | - try { | |
265 | - byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(Transaction.txID)); | |
266 | - byte[] expandedPrv = Utils.BigIntegerToBytes(keys[i]); | |
267 | - byte[] priKey = ExpandedPrivateKey.ExpandedPrivateKey(expandedPrv); | |
268 | - byte[] sig = Signer.Ed25519InnerSign(priKey, message); | |
269 | - input.witnessComponent.signatures[i] = Hex.toHexString(sig); | |
270 | - | |
271 | - } catch (Exception e) { | |
272 | - e.printStackTrace(); | |
273 | - } | |
274 | - } else { | |
275 | - System.out.println("generate signatures failed."); | |
276 | - } | |
277 | - } | |
278 | - return Transaction; | |
279 | - } | |
280 | - | |
281 | - //根据主私钥对交易签名,生成序列化的rawTransaction | |
282 | - public String rawTransaction(String rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
283 | - byte[] privateChild; | |
284 | - for (int i = 0; i < tx.inputs.size(); i++) { | |
285 | - if (tx.inputs.get(i).type == 1) { | |
286 | - privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, 1, tx.inputs.get(i).isChange(), tx.inputs.get(i).getControlProgramIndex()); | |
287 | - } else { | |
288 | - privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, tx.inputs.get(i).getKeyIndex()); | |
289 | - } | |
290 | - buildWitness(tx, i, privateChild); | |
291 | - } | |
292 | - return serializeTransaction(tx); | |
293 | - } | |
294 | - | |
295 | - //多签 | |
296 | - public String rawTransaction(String[] rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
297 | - String privateChild; | |
298 | - for (int i = 0; i < tx.inputs.size(); i++) { | |
299 | - if (tx.inputs.get(i).controlProgram.length() != 44) { | |
300 | - buildWitness(tx, i, rootPrivateKey); | |
301 | - } | |
302 | - | |
303 | - } | |
304 | - return serializeTransaction(tx); | |
305 | - } | |
306 | - | |
307 | - | |
308 | -} | |
309 | - | |
310 | - |
@@ -10,8 +10,7 @@ import java.util.Arrays; | ||
10 | 10 | |
11 | 11 | public class Signer { |
12 | 12 | |
13 | - public static byte[] Ed25519InnerSign(byte[] privateKey, byte[] message) | |
14 | - throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { | |
13 | + public static byte[] ed25519InnerSign(byte[] privateKey, byte[] message) throws NoSuchAlgorithmException { | |
15 | 14 | MessageDigest md = MessageDigest.getInstance("SHA-512"); |
16 | 15 | byte[] digestData = new byte[32 + message.length]; |
17 | 16 | int digestDataIndex = 0; |
@@ -0,0 +1,136 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +import io.bytom.common.*; | |
4 | +import io.bytom.types.*; | |
5 | +import org.bouncycastle.util.encoders.Hex; | |
6 | +import java.io.ByteArrayOutputStream; | |
7 | +import java.io.IOException; | |
8 | +import java.util.Map; | |
9 | + | |
10 | +public class SpendInput extends BaseInput { | |
11 | + | |
12 | + private String sourceId; | |
13 | + | |
14 | + private long sourcePosition; | |
15 | + | |
16 | + private boolean change; | |
17 | + | |
18 | + private int controlProgramIndex; | |
19 | + | |
20 | + private BIPProtocol bipProtocol; | |
21 | + | |
22 | + public SpendInput() {} | |
23 | + | |
24 | + public SpendInput(String assetID, long amount, String controlProgram) { | |
25 | + this.setAssetId(assetID); | |
26 | + this.setAmount(amount); | |
27 | + this.setProgram(controlProgram); | |
28 | + this.bipProtocol = BIPProtocol.BIP44; | |
29 | + } | |
30 | + | |
31 | + @Override | |
32 | + public InputEntry convertInputEntry(Map<Hash, Entry> entryMap, int index) { | |
33 | + Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram())); | |
34 | + AssetAmount assetAmount = this.getAssetAmount(); | |
35 | + Hash sourceID = new Hash(this.sourceId); | |
36 | + ValueSource src = new ValueSource(sourceID, assetAmount, this.sourcePosition); | |
37 | + | |
38 | + OutputEntry prevout = new OutputEntry(src, pro, 0); | |
39 | + Hash prevOutID = prevout.entryID(); | |
40 | + entryMap.put(prevOutID, prevout); | |
41 | + return new Spend(prevOutID, index); | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + public byte[] serializeInput() throws IOException { | |
46 | + ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
47 | + //assetVersion | |
48 | + Utils.writeVarint(1, stream); //AssetVersion是否默认为1 | |
49 | + | |
50 | + //inputCommitment | |
51 | + ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream(); | |
52 | + //spend type flag | |
53 | + Utils.writeVarint(SPEND_INPUT_TYPE, inputCommitStream); | |
54 | + //spendCommitment | |
55 | + ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream(); | |
56 | + spendCommitSteam.write(Hex.decode(sourceId)); //计算muxID | |
57 | + spendCommitSteam.write(Hex.decode(getAssetId())); | |
58 | + Utils.writeVarint(getAmount(), spendCommitSteam); | |
59 | + //sourcePosition | |
60 | + Utils.writeVarint(sourcePosition, spendCommitSteam); //db中获取position | |
61 | + //vmVersion | |
62 | + Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version | |
63 | + //controlProgram | |
64 | + Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam); | |
65 | + | |
66 | + byte[] dataSpendCommit = spendCommitSteam.toByteArray(); | |
67 | + | |
68 | + Utils.writeVarint(dataSpendCommit.length, inputCommitStream); | |
69 | + inputCommitStream.write(dataSpendCommit); | |
70 | + byte[] dataInputCommit = inputCommitStream.toByteArray(); | |
71 | + //inputCommit的length | |
72 | + Utils.writeVarint(dataInputCommit.length, stream); | |
73 | + stream.write(dataInputCommit); | |
74 | + | |
75 | + //inputWitness | |
76 | + if (null != getWitnessComponent()) { | |
77 | + ByteArrayOutputStream witnessStream = new ByteArrayOutputStream(); | |
78 | + //arguments | |
79 | + int witnessSize = getWitnessComponent().size(); | |
80 | + //arguments的length | |
81 | + Utils.writeVarint(witnessSize, witnessStream); | |
82 | + for (int i = 0; i < witnessSize; i++) { | |
83 | + String witness = getWitnessComponent().getWitness(i); | |
84 | + Utils.writeVarStr(Hex.decode(witness), witnessStream); | |
85 | + } | |
86 | + byte[] dataWitnessComponents = witnessStream.toByteArray(); | |
87 | + //witness的length | |
88 | + Utils.writeVarint(dataWitnessComponents.length, stream); | |
89 | + stream.write(dataWitnessComponents); | |
90 | + } | |
91 | + return stream.toByteArray(); | |
92 | + } | |
93 | + | |
94 | + @Override | |
95 | + public void buildWitness(String txID) throws Exception { | |
96 | + String rootPrvKey = getWitnessComponent().getRootPrivateKey(); | |
97 | + | |
98 | + byte[] privateChild; | |
99 | + if (bipProtocol == BIPProtocol.BIP44) { | |
100 | + privateChild = DerivePrivateKey.bip44derivePrvKey(rootPrvKey, TransactionSigner.AccountKeySpace, change, controlProgramIndex); | |
101 | + } else { | |
102 | + privateChild = DerivePrivateKey.bip32derivePrvKey(rootPrvKey, getKeyIndex(), TransactionSigner.AccountKeySpace, controlProgramIndex); | |
103 | + } | |
104 | + | |
105 | + byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID)); | |
106 | + byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(privateChild); | |
107 | + byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message); | |
108 | + | |
109 | + getWitnessComponent().appendWitness(Hex.toHexString(sig)); | |
110 | + | |
111 | + byte[] deriveXpub = DeriveXpub.deriveXpub(privateChild); | |
112 | + String pubKey = Hex.toHexString(deriveXpub).substring(0, 64); | |
113 | + | |
114 | + getWitnessComponent().appendWitness(pubKey); | |
115 | + } | |
116 | + | |
117 | + public void setSourceId(String sourceId) { | |
118 | + this.sourceId = sourceId; | |
119 | + } | |
120 | + | |
121 | + public void setSourcePosition(long sourcePosition) { | |
122 | + this.sourcePosition = sourcePosition; | |
123 | + } | |
124 | + | |
125 | + public void setChange(boolean change) { | |
126 | + this.change = change; | |
127 | + } | |
128 | + | |
129 | + public void setControlProgramIndex(int controlProgramIndex) { | |
130 | + this.controlProgramIndex = controlProgramIndex; | |
131 | + } | |
132 | + | |
133 | + public void setBipProtocol(BIPProtocol bipProtocol) { | |
134 | + this.bipProtocol = bipProtocol; | |
135 | + } | |
136 | +} | |
\ No newline at end of file |
@@ -1,14 +1,11 @@ | ||
1 | 1 | package io.bytom.api; |
2 | 2 | |
3 | -import com.google.gson.annotations.SerializedName; | |
4 | -import io.bytom.common.ParameterizedTypeImpl; | |
5 | -import io.bytom.common.SuccessRespon; | |
6 | 3 | import io.bytom.common.Utils; |
7 | -import io.bytom.exception.BytomException; | |
8 | -import io.bytom.http.Client; | |
9 | -import org.apache.log4j.Logger; | |
4 | +import io.bytom.types.*; | |
5 | +import org.bouncycastle.util.encoders.Hex; | |
10 | 6 | |
11 | -import java.lang.reflect.Type; | |
7 | +import java.io.ByteArrayOutputStream; | |
8 | +import java.io.IOException; | |
12 | 9 | import java.util.ArrayList; |
13 | 10 | import java.util.HashMap; |
14 | 11 | import java.util.List; |
@@ -20,7 +17,6 @@ import java.util.Map; | ||
20 | 17 | |
21 | 18 | public class Transaction { |
22 | 19 | |
23 | - @SerializedName("tx_id") | |
24 | 20 | public String txID; |
25 | 21 | /** |
26 | 22 | * version |
@@ -33,321 +29,170 @@ public class Transaction { | ||
33 | 29 | /** |
34 | 30 | * time_range |
35 | 31 | */ |
36 | - @SerializedName("time_range") | |
37 | 32 | public Integer timeRange; |
38 | 33 | |
39 | 34 | /** |
40 | - * status | |
41 | - */ | |
42 | - public Integer fee; | |
43 | - | |
44 | - /** | |
45 | 35 | * List of specified inputs for a transaction. |
46 | 36 | */ |
47 | - public List<AnnotatedInput> inputs; | |
37 | + public List<BaseInput> inputs; | |
48 | 38 | |
49 | 39 | /** |
50 | 40 | * List of specified outputs for a transaction. |
51 | 41 | */ |
52 | - public List<AnnotatedOutput> outputs; | |
53 | - | |
54 | - // public InputWitnessComponent inputWitnessComponent; | |
55 | - private static Logger logger = Logger.getLogger(Transaction.class); | |
56 | - | |
57 | - public String toJson() { | |
58 | - return Utils.serializer.toJson(this); | |
42 | + public List<Output> outputs; | |
43 | + | |
44 | + public Transaction(Builder builder) { | |
45 | + this.inputs = builder.inputs; | |
46 | + this.outputs = builder.outputs; | |
47 | + this.version = builder.version; | |
48 | + this.size = builder.size; | |
49 | + this.timeRange = builder.timeRange; | |
50 | + mapTx(); | |
51 | + sign(); | |
59 | 52 | } |
60 | 53 | |
61 | - public static Transaction fromJson(String json) { | |
62 | - return Utils.serializer.fromJson(json, Transaction.class); | |
63 | - } | |
64 | - | |
65 | - public static Transaction fromSuccessRespon(String json) { | |
66 | - Type responType = new ParameterizedTypeImpl(SuccessRespon.class, new Class[]{Transaction.class}); | |
67 | - SuccessRespon<Transaction> result = Utils.serializer.fromJson(json, responType); | |
68 | - return result.dataObject; | |
69 | - } | |
54 | + public static class Builder { | |
70 | 55 | |
71 | - public static Transaction decode(Client client, String txId) throws BytomException { | |
72 | - Map<String, Object> req = new HashMap<String, Object>(); | |
73 | - req.put("raw_transaction", txId); | |
74 | - Transaction Transaction = | |
75 | - client.request("decode-raw-transaction", req, Transaction.class); | |
56 | + private String txID; | |
76 | 57 | |
77 | - logger.info("decode-raw-transaction:"); | |
78 | - logger.info(Transaction.toJson()); | |
58 | + private Integer version = 1; | |
79 | 59 | |
80 | - return Transaction; | |
81 | - } | |
60 | + private Integer size = 0; | |
82 | 61 | |
83 | - public static class Builder { | |
84 | - @SerializedName("tx_id") | |
85 | - public String txID; | |
86 | - public Integer version; | |
87 | - public Integer size; | |
88 | - @SerializedName("time_range") | |
89 | - public Integer timeRange; | |
62 | + private Integer timeRange; | |
90 | 63 | |
91 | - Transaction tx; | |
92 | - List<AnnotatedInput> inputs; | |
93 | - List<AnnotatedOutput> outputs; | |
64 | + private List<BaseInput> inputs; | |
65 | + private List<Output> outputs; | |
94 | 66 | |
95 | 67 | public Builder() { |
96 | 68 | this.inputs = new ArrayList<>(); |
97 | 69 | this.outputs = new ArrayList<>(); |
98 | 70 | } |
99 | 71 | |
100 | - public Builder addInput(AnnotatedInput input) { | |
72 | + public Builder addInput(BaseInput input) { | |
101 | 73 | this.inputs.add(input); |
102 | 74 | return this; |
103 | 75 | } |
104 | 76 | |
105 | - public Builder addOutput(AnnotatedOutput output) { | |
77 | + public Builder addOutput(Output output) { | |
106 | 78 | this.outputs.add(output); |
107 | 79 | return this; |
108 | 80 | } |
109 | 81 | |
110 | - | |
111 | - public Transaction build(Integer version, Integer timeRange, Integer size) { | |
112 | - tx = new Transaction(); | |
113 | - tx.inputs = this.inputs; | |
114 | - tx.outputs = this.outputs; | |
115 | - tx.version = version; | |
116 | - tx.timeRange = timeRange; | |
117 | - tx.size = size; | |
118 | - return tx; | |
119 | - } | |
120 | - | |
121 | - public Transaction build(int timeRange) { | |
122 | - tx = new Transaction(); | |
123 | - tx.inputs = this.inputs; | |
124 | - tx.outputs = this.outputs; | |
125 | - tx.version = 1; | |
126 | - tx.size = 0; | |
127 | - tx.timeRange = timeRange; | |
128 | - return tx; | |
129 | - } | |
130 | - } | |
131 | - | |
132 | - public static class AnnotatedInput { | |
133 | - | |
134 | - @SerializedName("input_id") | |
135 | - public String inputID; | |
136 | - /** | |
137 | - * address | |
138 | - */ | |
139 | - public String address; | |
140 | - | |
141 | - /** | |
142 | - * The number of units of the asset being issued or spent. | |
143 | - */ | |
144 | - public long amount; | |
145 | - | |
146 | - // /** | |
147 | -// * The definition of the asset being issued or spent (possibly null). | |
148 | -// */ | |
149 | -// @SerializedName("asset_definition") | |
150 | -// private Map<String, Object> assetDefinition; | |
151 | - @SerializedName("asset_definition") | |
152 | - public String assetDefinition; | |
153 | - | |
154 | - /** | |
155 | - * The id of the asset being issued or spent. | |
156 | - */ | |
157 | - @SerializedName("asset_id") | |
158 | - public String assetId; | |
159 | - | |
160 | - /** | |
161 | - * The control program which must be satisfied to transfer this output. | |
162 | - */ | |
163 | - @SerializedName("control_program") | |
164 | - public String controlProgram; | |
165 | - | |
166 | - /** | |
167 | - * The id of the output consumed by this input. Null if the input is an | |
168 | - * issuance. | |
169 | - */ | |
170 | - @SerializedName("spent_output_id") | |
171 | - public String spentOutputId; | |
172 | - | |
173 | - /** | |
174 | - * The type of the input.<br> | |
175 | - * Possible values are "issue" and "spend". | |
176 | - */ | |
177 | - public int type; | |
178 | - | |
179 | - public String sourceId; | |
180 | - | |
181 | - public long sourcePosition; | |
182 | - | |
183 | - public String nonce; | |
184 | - | |
185 | - private int controlProgramIndex; | |
186 | - private boolean change; | |
187 | - | |
188 | - public int keyIndex; | |
189 | - public String chainPath; | |
190 | - | |
191 | - @SerializedName("witness_component") | |
192 | - public InputWitnessComponent witnessComponent; | |
193 | - | |
194 | - @Override | |
195 | - public String toString() { | |
196 | - return Utils.serializer.toJson(this); | |
197 | - } | |
198 | - | |
199 | - public int getControlProgramIndex() { | |
200 | - return controlProgramIndex; | |
201 | - } | |
202 | - | |
203 | - public AnnotatedInput setControlProgramIndex(int controlProgramIndex) { | |
204 | - this.controlProgramIndex = controlProgramIndex; | |
82 | + public Builder setTimeRange(int timeRange) { | |
83 | + this.timeRange = timeRange; | |
205 | 84 | return this; |
206 | 85 | } |
207 | 86 | |
208 | - public boolean isChange() { | |
209 | - return change; | |
210 | - } | |
211 | - | |
212 | - public AnnotatedInput setChange(boolean change) { | |
213 | - this.change = change; | |
214 | - return this; | |
215 | - } | |
216 | - | |
217 | - public AnnotatedInput setAmount(long amount) { | |
218 | - this.amount = amount; | |
219 | - return this; | |
87 | + public Transaction build() { | |
88 | + return new Transaction(this); | |
220 | 89 | } |
221 | - | |
222 | - public AnnotatedInput setAssetId(String assetId) { | |
223 | - this.assetId = assetId; | |
224 | - return this; | |
225 | - } | |
226 | - | |
227 | - public AnnotatedInput setControlProgram(String controlProgram) { | |
228 | - this.controlProgram = controlProgram; | |
229 | - return this; | |
230 | - } | |
231 | - | |
232 | - public AnnotatedInput setType(int type) { | |
233 | - this.type = type; | |
234 | - return this; | |
235 | - } | |
236 | - | |
237 | - public AnnotatedInput setSourceId(String sourceId) { | |
238 | - this.sourceId = sourceId; | |
239 | - return this; | |
240 | - } | |
241 | - | |
242 | - public AnnotatedInput setSourcePosition(long sourcePosition) { | |
243 | - this.sourcePosition = sourcePosition; | |
244 | - return this; | |
245 | - } | |
246 | - | |
247 | - public AnnotatedInput setNonce(String nonce) { | |
248 | - this.nonce = nonce; | |
249 | - return this; | |
250 | - } | |
251 | - | |
252 | - public AnnotatedInput setAssetDefinition(String assetDefinition) { | |
253 | - this.assetDefinition = assetDefinition; | |
254 | - return this; | |
255 | - } | |
256 | - | |
257 | - public int getKeyIndex() { | |
258 | - return keyIndex; | |
259 | - } | |
260 | - | |
261 | - public AnnotatedInput setKeyIndex(int keyIndex) { | |
262 | - this.keyIndex = keyIndex; | |
263 | - return this; | |
264 | - } | |
265 | - | |
266 | 90 | } |
267 | 91 | |
268 | - public static class AnnotatedOutput { | |
269 | - | |
270 | - /** | |
271 | - * address | |
272 | - */ | |
273 | - public String address; | |
274 | - | |
275 | - /** | |
276 | - * The number of units of the asset being controlled. | |
277 | - */ | |
278 | - public long amount; | |
279 | - | |
280 | - /** | |
281 | - * The definition of the asset being controlled (possibly null). | |
282 | - */ | |
283 | - @SerializedName("asset_definition") | |
284 | - public Map<String, Object> assetDefinition; | |
285 | - | |
286 | - /** | |
287 | - * The id of the asset being controlled. | |
288 | - */ | |
289 | - @SerializedName("asset_id") | |
290 | - public String assetId; | |
291 | - | |
292 | - /** | |
293 | - * The control program which must be satisfied to transfer this output. | |
294 | - */ | |
295 | - @SerializedName("control_program") | |
296 | - public String controlProgram; | |
297 | - | |
298 | - /** | |
299 | - * The id of the output. | |
300 | - */ | |
301 | - @SerializedName("id") | |
302 | - public String id; | |
303 | - | |
304 | - /** | |
305 | - * The output's position in a transaction's list of outputs. | |
306 | - */ | |
307 | - public Integer position; | |
308 | - | |
309 | - /** | |
310 | - * The type the output.<br> | |
311 | - * Possible values are "control" and "retire". | |
312 | - */ | |
313 | - public String type; | |
314 | - | |
315 | - public AnnotatedOutput setAddress(String address) { | |
316 | - this.address = address; | |
317 | - return this; | |
318 | - } | |
319 | - | |
320 | - public AnnotatedOutput setAmount(long amount) { | |
321 | - this.amount = amount; | |
322 | - return this; | |
323 | - } | |
324 | - | |
325 | - public AnnotatedOutput setAssetId(String assetId) { | |
326 | - this.assetId = assetId; | |
327 | - return this; | |
92 | + private void sign() { | |
93 | + for (BaseInput input : inputs) { | |
94 | + try { | |
95 | + input.buildWitness(txID); | |
96 | + } catch (Exception e) { | |
97 | + e.printStackTrace(); | |
98 | + throw new RuntimeException(e); | |
99 | + } | |
328 | 100 | } |
101 | + } | |
329 | 102 | |
330 | - public AnnotatedOutput setControlProgram(String controlProgram) { | |
331 | - this.controlProgram = controlProgram; | |
332 | - return this; | |
333 | - } | |
103 | + public String rawTransaction() { | |
104 | + String rawTransaction; | |
105 | + //开始序列化 | |
106 | + ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
107 | + try { | |
108 | + stream.write(7); | |
109 | + // version | |
110 | + if (null != version) | |
111 | + Utils.writeVarint(version, stream); | |
112 | + if (null != timeRange) | |
113 | + Utils.writeVarint(timeRange, stream); | |
114 | + //inputs | |
115 | + if (null != inputs && inputs.size() > 0) { | |
116 | + Utils.writeVarint(inputs.size(), stream); | |
117 | + for (BaseInput input : inputs) { | |
118 | + System.out.println(Hex.toHexString(input.serializeInput())); | |
119 | + stream.write(input.serializeInput()); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + //outputs | |
124 | + if (null != outputs && outputs.size() > 0) { | |
125 | + Utils.writeVarint(outputs.size(), stream); | |
126 | + for (Output output : outputs) { | |
127 | + stream.write(output.serializeOutput()); | |
128 | + } | |
129 | + } | |
130 | + byte[] data = stream.toByteArray(); | |
131 | + rawTransaction = Hex.toHexString(data); | |
132 | + } catch (IOException e) { | |
133 | + throw new RuntimeException(e); | |
134 | + } | |
135 | + return rawTransaction; | |
136 | + } | |
334 | 137 | |
335 | - public AnnotatedOutput setPosition(Integer position) { | |
336 | - this.position = position; | |
337 | - return this; | |
138 | + private void mapTx() { | |
139 | + Map<Hash, Entry> entryMap = new HashMap<>(); | |
140 | + ValueSource[] muxSources = new ValueSource[inputs.size()]; | |
141 | + List<InputEntry> inputEntries = new ArrayList<>(); | |
142 | + | |
143 | + try { | |
144 | + for (int i = 0; i < inputs.size(); i++) { | |
145 | + BaseInput input = inputs.get(i); | |
146 | + InputEntry inputEntry = input.convertInputEntry(entryMap, i); | |
147 | + Hash spendID = addEntry(entryMap, inputEntry); | |
148 | + input.setInputID(spendID.toString()); | |
149 | + | |
150 | + muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0); | |
151 | + inputEntries.add(inputEntry); | |
152 | + } | |
153 | + | |
154 | + Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51})); | |
155 | + Hash muxID = addEntry(entryMap, mux); | |
156 | + for (InputEntry inputEntry : inputEntries) { | |
157 | + inputEntry.setDestination(muxID, inputEntry.ordinal, entryMap); | |
158 | + } | |
159 | + | |
160 | + List<Hash> resultIDList = new ArrayList<>(); | |
161 | + for (int i = 0; i < outputs.size(); i++) { | |
162 | + Output output = outputs.get(i); | |
163 | + | |
164 | + AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount); | |
165 | + ValueSource src = new ValueSource(muxID, amount, i); | |
166 | + | |
167 | + Hash resultID; | |
168 | + if (output.controlProgram.startsWith("6a")) { | |
169 | + Retirement retirement = new Retirement(src, i); | |
170 | + resultID = addEntry(entryMap, retirement); | |
171 | + } else { | |
172 | + Program prog = new Program(1, Hex.decode(output.controlProgram)); | |
173 | + OutputEntry oup = new OutputEntry(src, prog, i); | |
174 | + resultID = addEntry(entryMap, oup); | |
175 | + } | |
176 | + | |
177 | + resultIDList.add(resultID); | |
178 | + output.id = resultID.toString(); | |
179 | + | |
180 | + ValueDestination destination = new ValueDestination(resultID, src.value, 0); | |
181 | + mux.witnessDestinations.add(destination); | |
182 | + } | |
183 | + | |
184 | + TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{})); | |
185 | + Hash txID = addEntry(entryMap, txHeader); | |
186 | + this.txID = txID.toString(); | |
187 | + | |
188 | + } catch (Exception e) { | |
189 | + throw new RuntimeException(e); | |
338 | 190 | } |
339 | 191 | } |
340 | 192 | |
341 | - /** | |
342 | - * A single witness component, holding information that will become the input | |
343 | - * witness. | |
344 | - */ | |
345 | - public static class InputWitnessComponent { | |
346 | - | |
347 | - /** | |
348 | - * The list of signatures made with the specified keys (null unless type is | |
349 | - * "signature"). | |
350 | - */ | |
351 | - public String[] signatures; | |
193 | + private Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) { | |
194 | + Hash id = entry.entryID(); | |
195 | + entryMap.put(id, entry); | |
196 | + return id; | |
352 | 197 | } |
353 | 198 | } |
@@ -0,0 +1,12 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +/** | |
4 | + * Created by liqiang on 2018/10/24. | |
5 | + */ | |
6 | +public class TransactionSigner { | |
7 | + | |
8 | + public static final byte AssetKeySpace = 0; | |
9 | + public static final byte AccountKeySpace = 1; | |
10 | +} | |
11 | + | |
12 | + |
@@ -61,15 +61,15 @@ public class UTXO { | ||
61 | 61 | return Utils.serializer.fromJson(json, UTXO.class); |
62 | 62 | } |
63 | 63 | |
64 | - public static Transaction.AnnotatedInput utxoToAnnotatedInput(UTXO utxo) { | |
65 | - Transaction.AnnotatedInput annotatedInput = new Transaction.AnnotatedInput(); | |
66 | - annotatedInput.setAmount(utxo.amount); | |
67 | - annotatedInput.setControlProgram(utxo.program); | |
68 | - annotatedInput.setChange(utxo.change); | |
69 | - annotatedInput.setAssetId(utxo.assetId); | |
70 | - annotatedInput.setControlProgramIndex(utxo.controlProgramIndex); | |
71 | - annotatedInput.setSourceId(utxo.sourceId); | |
72 | - annotatedInput.setSourcePosition(utxo.sourcePos); | |
73 | - return annotatedInput; | |
64 | + public SpendInput toSpendAnnotatedInput() { | |
65 | + SpendInput spendInput = new SpendInput(); | |
66 | + spendInput.setAmount(amount); | |
67 | + spendInput.setProgram(program); | |
68 | + spendInput.setChange(change); | |
69 | + spendInput.setAssetId(assetId); | |
70 | + spendInput.setControlProgramIndex(controlProgramIndex); | |
71 | + spendInput.setSourceId(sourceId); | |
72 | + spendInput.setSourcePosition(sourcePos); | |
73 | + return spendInput; | |
74 | 74 | } |
75 | 75 | } |
@@ -0,0 +1,43 @@ | ||
1 | +package io.bytom.api; | |
2 | + | |
3 | +import java.util.ArrayList; | |
4 | +import java.util.List; | |
5 | + | |
6 | +/** | |
7 | + * A single witness component, holding information that will become the input | |
8 | + * witness. | |
9 | + */ | |
10 | +public class WitnessComponent { | |
11 | + | |
12 | + /** | |
13 | + * The list of witnesses made with the specified keys (null unless type is | |
14 | + * "signature"). | |
15 | + */ | |
16 | + private List<String> witnesses; | |
17 | + | |
18 | + private String rootPrivateKey; | |
19 | + | |
20 | + public WitnessComponent() { | |
21 | + witnesses = new ArrayList<>(); | |
22 | + } | |
23 | + | |
24 | + public int size() { | |
25 | + return witnesses.size(); | |
26 | + } | |
27 | + | |
28 | + public String getWitness(int index) { | |
29 | + return witnesses.get(index); | |
30 | + } | |
31 | + | |
32 | + public void appendWitness(String witness) { | |
33 | + witnesses.add(witness); | |
34 | + } | |
35 | + | |
36 | + public String getRootPrivateKey() { | |
37 | + return rootPrivateKey; | |
38 | + } | |
39 | + | |
40 | + public void setRootPrivateKey(String rootPrivateKey) { | |
41 | + this.rootPrivateKey = rootPrivateKey; | |
42 | + } | |
43 | +} | |
\ No newline at end of file |
@@ -1,13 +0,0 @@ | ||
1 | -package io.bytom.common; | |
2 | - | |
3 | -/** | |
4 | - * Created by liqiang on 2018/10/17. | |
5 | - */ | |
6 | -public class Constants { | |
7 | - | |
8 | - public static int INPUT_TYPE_ISSUANCE = 0; | |
9 | - | |
10 | - public static int INPUT_TYPE_SPEND = 1; | |
11 | - | |
12 | - public static int INPUT_TYPE_COINBASE = 2; | |
13 | -} |
@@ -10,25 +10,23 @@ import java.security.SignatureException; | ||
10 | 10 | public class DerivePrivateKey { |
11 | 11 | //bip44 派生子秘钥 |
12 | 12 | //accountIndex 默认为1 |
13 | - public static byte[] derivePrivateKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
14 | - byte[] xprv = Hex.decode(rootPriv); | |
13 | + public static byte[] bip44derivePrvKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
15 | 14 | byte[][] paths = PathUtil.getBip44Path(accountIndex, change, programIndex); |
16 | - byte[] res = xprv; | |
15 | + byte[] res = Hex.decode(rootPriv); | |
17 | 16 | for (int i = 0; i < paths.length; i++) { |
18 | 17 | byte[] xpub = DeriveXpub.deriveXpub(res); |
19 | - res = NonHardenedChild.NHchild(paths[i], res, xpub); | |
18 | + res = NonHardenedChild.nhChild(paths[i], res, xpub); | |
20 | 19 | } |
21 | 20 | return res; |
22 | 21 | } |
23 | 22 | |
24 | 23 | //BIP32 派生子秘钥 issue 需要用到BIP32派生规则 |
25 | - public static byte[] derivePrivateKey(String rootPrivateKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
26 | - byte[] xprv = Hex.decode(rootPrivateKey); | |
27 | - byte[] res = xprv; | |
28 | - byte[][] paths = PathUtil.getBip32Path(keyIndex); | |
24 | + public static byte[] bip32derivePrvKey(String rootPrivateKey, int accountIndex, byte keySpace, long ...programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
25 | + byte[] res = Hex.decode(rootPrivateKey); | |
26 | + byte[][] paths = PathUtil.getBip32Path(keySpace, accountIndex, programIndex); | |
29 | 27 | for (int i = 0; i < paths.length; i++) { |
30 | 28 | byte[] xpub = DeriveXpub.deriveXpub(res); |
31 | - res = NonHardenedChild.NHchild(paths[i], res, xpub); | |
29 | + res = NonHardenedChild.nhChild(paths[i], res, xpub); | |
32 | 30 | } |
33 | 31 | return res; |
34 | 32 | } |
@@ -6,18 +6,11 @@ public class DeriveXpub { | ||
6 | 6 | public static byte[] deriveXpub(byte[] xprv) { |
7 | 7 | byte[] xpub = new byte[xprv.length]; |
8 | 8 | byte[] scalar = new byte[xprv.length / 2]; |
9 | -// for (int i = 0; i < xprv.length / 2; i++) { | |
10 | -// scalar[i] = xprv[i]; | |
11 | -// } | |
9 | + | |
12 | 10 | System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2); |
13 | 11 | byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar); |
14 | -// for (int i = 0; i < buf.length; i++) { | |
15 | -// xpub[i] = buf[i]; | |
16 | -// } | |
12 | + | |
17 | 13 | System.arraycopy(buf, 0, xpub, 0, buf.length); |
18 | -// for (int i = xprv.length / 2; i < xprv.length; i++) { | |
19 | -// xpub[i] = xprv[i]; | |
20 | -// } | |
21 | 14 | System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2); |
22 | 15 | return xpub; |
23 | 16 | } |
@@ -9,7 +9,7 @@ import java.security.NoSuchAlgorithmException; | ||
9 | 9 | import java.security.SignatureException; |
10 | 10 | |
11 | 11 | public class ExpandedPrivateKey { |
12 | - public static byte[] HMacSha512(byte[] data, byte[] key) | |
12 | + public static byte[] hmacSha512(byte[] data, byte[] key) | |
13 | 13 | throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { |
14 | 14 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512"); |
15 | 15 | Mac mac = Mac.getInstance("HmacSHA512"); |
@@ -17,10 +17,10 @@ public class ExpandedPrivateKey { | ||
17 | 17 | return mac.doFinal(data); |
18 | 18 | } |
19 | 19 | |
20 | - public static byte[] ExpandedPrivateKey(byte[] data) | |
20 | + public static byte[] expandedPrivateKey(byte[] data) | |
21 | 21 | throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { |
22 | 22 | // "457870616e64" is "Expand" hex. |
23 | - byte[] res = HMacSha512(data, Hex.decode("457870616e64")); | |
23 | + byte[] res = hmacSha512(data, Hex.decode("457870616e64")); | |
24 | 24 | for (int i = 0; i <= 31; i++) { |
25 | 25 | res[i] = data[i]; |
26 | 26 | } |
@@ -11,7 +11,7 @@ import java.security.SignatureException; | ||
11 | 11 | |
12 | 12 | public class NonHardenedChild { |
13 | 13 | |
14 | - private static byte[] HMacSha512(byte[] data, byte[] key) | |
14 | + private static byte[] hmacSha512(byte[] data, byte[] key) | |
15 | 15 | throws NoSuchAlgorithmException, InvalidKeyException { |
16 | 16 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512"); |
17 | 17 | Mac mac = Mac.getInstance("HmacSHA512"); |
@@ -19,7 +19,7 @@ public class NonHardenedChild { | ||
19 | 19 | return mac.doFinal(data); |
20 | 20 | } |
21 | 21 | |
22 | - public static byte[] NHchild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
22 | + public static byte[] nhChild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
23 | 23 | // begin build data |
24 | 24 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
25 | 25 | out.write('N'); |
@@ -34,7 +34,7 @@ public class NonHardenedChild { | ||
34 | 34 | // end build key |
35 | 35 | |
36 | 36 | // doFinal() |
37 | - byte[] res = HMacSha512(data, key); | |
37 | + byte[] res = hmacSha512(data, key); | |
38 | 38 | |
39 | 39 | //begin operate res[:32] |
40 | 40 | byte[] f = new byte[res.length / 2]; |
@@ -76,7 +76,7 @@ public class NonHardenedChild { | ||
76 | 76 | byte[] res = xprv; |
77 | 77 | for (int i = 0; i < hpaths.length; i++) { |
78 | 78 | byte[] xpub = DeriveXpub.deriveXpub(res); |
79 | - res = NonHardenedChild.NHchild(paths[i], res, xpub); | |
79 | + res = NonHardenedChild.nhChild(paths[i], res, xpub); | |
80 | 80 | } |
81 | 81 | return res; |
82 | 82 | } |
@@ -3,6 +3,7 @@ package io.bytom.common; | ||
3 | 3 | import com.google.gson.Gson; |
4 | 4 | import com.google.gson.GsonBuilder; |
5 | 5 | import io.bytom.types.ExpandedKeys; |
6 | +import org.bouncycastle.jcajce.provider.digest.SHA3; | |
6 | 7 | import org.bouncycastle.util.encoders.Hex; |
7 | 8 | |
8 | 9 | import java.io.ByteArrayOutputStream; |
@@ -76,7 +77,7 @@ public class Utils { | ||
76 | 77 | } |
77 | 78 | |
78 | 79 | public static ExpandedKeys expandedPriKey(String priKey, String key) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
79 | - byte[] hashPriKey = ExpandedPrivateKey.HMacSha512(Hex.decode(priKey), key.getBytes()); | |
80 | + byte[] hashPriKey = ExpandedPrivateKey.hmacSha512(Hex.decode(priKey), key.getBytes()); | |
80 | 81 | //begin operate res[:32] |
81 | 82 | byte[] f = new byte[hashPriKey.length / 2]; |
82 | 83 | System.arraycopy(hashPriKey, 0, f, 0, hashPriKey.length / 2); |
@@ -101,4 +102,15 @@ public class Utils { | ||
101 | 102 | return null; |
102 | 103 | |
103 | 104 | } |
105 | + | |
106 | + public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) { | |
107 | + | |
108 | + SHA3.Digest256 digest256 = new SHA3.Digest256(); | |
109 | + // data = hashedInputHex + txID | |
110 | + ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
111 | + out.write(hashedInputHex, 0, hashedInputHex.length); | |
112 | + out.write(txID, 0, txID.length); | |
113 | + byte[] data = out.toByteArray(); | |
114 | + return digest256.digest(data); | |
115 | + } | |
104 | 116 | } |
@@ -0,0 +1,100 @@ | ||
1 | +package io.bytom.common; | |
2 | + | |
3 | +import java.io.ByteArrayOutputStream; | |
4 | +import java.io.IOException; | |
5 | + | |
6 | +public class VMUtil { | |
7 | + | |
8 | + private static final byte OP_0 = (byte) 0x00; | |
9 | + private static final byte OP_1 = (byte) 0x51; | |
10 | + private static final byte OP_PUSHDATA1 = (byte) 0x4c; | |
11 | + private static final byte OP_PUSHDATA2 = (byte) 0x4d; | |
12 | + private static final byte OP_PUSHDATA4 = (byte) 0x43; | |
13 | + private static final byte OP_TXSIGHASH = (byte) 0xae; | |
14 | + private static final byte OP_CHECKMULTISIG = (byte) 0xad; | |
15 | + | |
16 | + public static byte[] p2spMultiSigProgram(byte[][] pubKeys, int nRequired) { | |
17 | + checkMultiSigParams(nRequired, pubKeys.length); | |
18 | + ByteArrayOutputStream program = new ByteArrayOutputStream(); | |
19 | + program.write(OP_TXSIGHASH); | |
20 | + | |
21 | + for (byte[] pubKey : pubKeys) { | |
22 | + try { | |
23 | + program.write(pushDataBytes(pubKey)); | |
24 | + program.write(pushDataInt64(nRequired)); | |
25 | + program.write(pushDataInt64(pubKeys.length)); | |
26 | + } catch (IOException e) { | |
27 | + e.printStackTrace(); | |
28 | + } | |
29 | + } | |
30 | + program.write(OP_CHECKMULTISIG); | |
31 | + return program.toByteArray(); | |
32 | + } | |
33 | + | |
34 | + private static void checkMultiSigParams(int nRequired, int nPubkeys) { | |
35 | + if (nRequired < 0 || nPubkeys < 0 || nRequired > nPubkeys || (nRequired == 0 && nPubkeys > 0)) { | |
36 | + throw new IllegalArgumentException(); | |
37 | + } | |
38 | + } | |
39 | + | |
40 | + private static byte[] pushDataBytes(byte[] data) { | |
41 | + int len = data.length; | |
42 | + if (len == 0) { | |
43 | + return new byte[] {OP_0}; | |
44 | + } | |
45 | + if (len <= 75) { | |
46 | + byte[] dest = new byte[1 + len]; | |
47 | + dest[0] = (byte) len; | |
48 | + System.arraycopy(data, 0, dest, 1, len); | |
49 | + return dest; | |
50 | + } | |
51 | + if (len < 256) { | |
52 | + byte[] dest = new byte[2 + len]; | |
53 | + dest[0] = OP_PUSHDATA1; | |
54 | + dest[1] = (byte) len; | |
55 | + System.arraycopy(data, 0, dest, 2, len); | |
56 | + return dest; | |
57 | + } | |
58 | + if (len < 65536) { | |
59 | + byte[] dest = new byte[3 + len]; | |
60 | + dest[0] = OP_PUSHDATA2; | |
61 | + dest[1] = (byte) len; | |
62 | + dest[2] = (byte) (len >> 8); | |
63 | + System.arraycopy(data, 0, dest, 3, len); | |
64 | + return dest; | |
65 | + } | |
66 | + byte[] dest = new byte[5 + len]; | |
67 | + dest[0] = OP_PUSHDATA4; | |
68 | + dest[1] = (byte) len; | |
69 | + dest[2] = (byte) (len >> 8); | |
70 | + dest[3] = (byte) (len >> 16); | |
71 | + dest[4] = (byte) (len >> 24); | |
72 | + System.arraycopy(data, 0, dest, 5, len); | |
73 | + return dest; | |
74 | + } | |
75 | + | |
76 | + private static byte[] pushDataInt64(long n) { | |
77 | + if (n == 0) { | |
78 | + return new byte[] {OP_0}; | |
79 | + } | |
80 | + if (n >= 1 && n <= 16) { | |
81 | + return new byte[] {(byte) (OP_1 + (byte) n - 1)}; | |
82 | + } | |
83 | + return pushDataBytes(int64Bytes(n)); | |
84 | + } | |
85 | + | |
86 | + private static byte[] int64Bytes(long n) { | |
87 | + byte[] bytes = new byte[8]; | |
88 | + int i = 0; | |
89 | + while (n != 0) { | |
90 | + bytes[i] = (byte) n; | |
91 | + n >>= 8; | |
92 | + i++; | |
93 | + } | |
94 | + byte[] res = new byte[i]; | |
95 | + System.arraycopy(bytes, 0, res, 0, i); | |
96 | + return res; | |
97 | + } | |
98 | + | |
99 | +} | |
100 | + |
@@ -69,7 +69,7 @@ public class Asset { | ||
69 | 69 | } |
70 | 70 | |
71 | 71 | private String computeIssueProgram(String rootKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
72 | - byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, keyIndex); | |
72 | + byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, keyIndex, (byte) 0, 1); | |
73 | 73 | byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey); |
74 | 74 | |
75 | 75 | String issueProgram = "ae20" + Hex.toHexString(deriveXpub).substring(0, 64) + "5151ad"; |
@@ -0,0 +1,10 @@ | ||
1 | +package io.bytom.types; | |
2 | + | |
3 | +import java.util.Map; | |
4 | + | |
5 | +public abstract class InputEntry extends Entry { | |
6 | + | |
7 | + public int ordinal; | |
8 | + | |
9 | + public abstract void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap); | |
10 | +} |
@@ -1,11 +1,11 @@ | ||
1 | 1 | package io.bytom.types; |
2 | 2 | |
3 | 3 | import java.io.ByteArrayOutputStream; |
4 | +import java.util.Map; | |
4 | 5 | |
5 | -public class Issue extends Entry { | |
6 | +public class Issue extends InputEntry { | |
6 | 7 | public Hash nonceHash; |
7 | 8 | public AssetAmount assetAmount; |
8 | - public int ordinal; | |
9 | 9 | public AssetDefinition assetDefinition; |
10 | 10 | public ValueDestination witnessDestination; |
11 | 11 |
@@ -15,8 +15,9 @@ public class Issue extends Entry { | ||
15 | 15 | this.ordinal = ordinal; |
16 | 16 | } |
17 | 17 | |
18 | - public void setDestination(Hash id, AssetAmount val, long pos) { | |
19 | - this.witnessDestination = new ValueDestination(id, val, pos); | |
18 | + @Override | |
19 | + public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) { | |
20 | + this.witnessDestination = new ValueDestination(id, this.assetAmount, pos); | |
20 | 21 | } |
21 | 22 | |
22 | 23 | @Override |
@@ -2,7 +2,7 @@ package io.bytom.types; | ||
2 | 2 | |
3 | 3 | import java.io.ByteArrayOutputStream; |
4 | 4 | |
5 | -public class Output extends Entry { | |
5 | +public class OutputEntry extends Entry { | |
6 | 6 | |
7 | 7 | public ValueSource source; |
8 | 8 |
@@ -10,13 +10,13 @@ public class Output extends Entry { | ||
10 | 10 | |
11 | 11 | public Integer ordinal; |
12 | 12 | |
13 | - public Output() { | |
13 | + public OutputEntry() { | |
14 | 14 | this.source = new ValueSource(); |
15 | 15 | this.controlProgram = new Program(); |
16 | 16 | } |
17 | 17 | |
18 | 18 | |
19 | - public Output(ValueSource source, Program controlProgram, Integer ordinal) { | |
19 | + public OutputEntry(ValueSource source, Program controlProgram, Integer ordinal) { | |
20 | 20 | this.source = source; |
21 | 21 | this.controlProgram = controlProgram; |
22 | 22 | this.ordinal = ordinal; |
@@ -1,8 +1,9 @@ | ||
1 | 1 | package io.bytom.types; |
2 | 2 | |
3 | 3 | import java.io.ByteArrayOutputStream; |
4 | +import java.util.Map; | |
4 | 5 | |
5 | -public class Spend extends Entry { | |
6 | +public class Spend extends InputEntry { | |
6 | 7 | |
7 | 8 | public Hash spentOutputID; |
8 | 9 |
@@ -17,8 +18,10 @@ public class Spend extends Entry { | ||
17 | 18 | this.ordinal = ordinal; |
18 | 19 | } |
19 | 20 | |
20 | - public void setDestination(Hash id, AssetAmount val, long pos) { | |
21 | - this.witnessDestination = new ValueDestination(id, val, pos); | |
21 | + @Override | |
22 | + public void setDestination(Hash id, long pos, Map<Hash, Entry> entryMap) { | |
23 | + OutputEntry spendOutput = (OutputEntry) entryMap.get(this.spentOutputID); | |
24 | + this.witnessDestination = new ValueDestination(id, spendOutput.source.value, pos); | |
22 | 25 | } |
23 | 26 | |
24 | 27 | @Override |
@@ -32,23 +32,27 @@ public class PathUtil { | ||
32 | 32 | if (change) { |
33 | 33 | changeStr = "01000000"; |
34 | 34 | } |
35 | - byte[][] paths = new byte[][]{ | |
35 | + return new byte[][]{ | |
36 | 36 | Hex.decode("2c000000"), |
37 | 37 | Hex.decode("99000000"), |
38 | 38 | Hex.decode(accountIndexStr), |
39 | 39 | Hex.decode(changeStr), |
40 | 40 | Hex.decode(programIndexStr), |
41 | 41 | }; |
42 | - return paths; | |
43 | 42 | } |
44 | 43 | |
45 | - public static byte[][] getBip32Path(int accountIndex) { | |
44 | + public static byte[][] getBip32Path(byte keySpace, long accountIndex, long ...itemIndexes) { | |
46 | 45 | byte[] signerPath = new byte[9]; |
47 | - byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putInt(accountIndex).array(); | |
46 | + signerPath[0] = keySpace; | |
47 | + byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(accountIndex).array(); | |
48 | 48 | System.arraycopy(path, 0, signerPath, 1, 8); |
49 | - byte[][] paths = new byte[][]{ | |
50 | - signerPath | |
51 | - }; | |
52 | - return paths; | |
49 | + | |
50 | + byte[][] res = new byte[1 + itemIndexes.length][]; | |
51 | + res[0] = signerPath; | |
52 | + for (int i = 0; i < itemIndexes.length; i++) { | |
53 | + path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(itemIndexes[i]).array(); | |
54 | + res[i + 1] = path; | |
55 | + } | |
56 | + return res; | |
53 | 57 | } |
54 | 58 | } |
@@ -1,83 +1,132 @@ | ||
1 | 1 | package io.bytom; |
2 | 2 | |
3 | 3 | import io.bytom.api.*; |
4 | -import io.bytom.exception.BytomException; | |
5 | -import io.bytom.http.Client; | |
6 | 4 | import org.bouncycastle.util.encoders.Hex; |
7 | 5 | import org.junit.Test; |
8 | 6 | |
9 | -import java.security.InvalidKeyException; | |
10 | -import java.security.NoSuchAlgorithmException; | |
11 | -import java.security.SignatureException; | |
12 | - | |
13 | 7 | public class AppTest { |
14 | 8 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; |
15 | 9 | String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; |
16 | 10 | |
17 | 11 | |
18 | 12 | @Test |
19 | - public void testSpend() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
20 | - Transaction.AnnotatedInput input = btmUtxoToInput(); | |
21 | - Transaction Transaction = new Transaction.Builder() | |
22 | -// .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"). | |
23 | -// setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0)) | |
24 | - .addInput(input.setType(1)) | |
25 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(49100000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b")) | |
26 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("0020fa56ca7d47f8528e68e120d0e052885faeb9d090d238fa4266bdde21b137513c")) | |
27 | - .build(200000); | |
28 | - io.bytom.api.Transaction transaction = MapTransaction.mapTx(Transaction); | |
29 | - SignTransaction signTransaction = new SignTransaction(); | |
30 | - String rawTransaction = signTransaction.rawTransaction(rootKey, transaction); | |
31 | - System.out.println(rawTransaction); | |
13 | + public void testSpendBIP44() { | |
14 | + SpendInput input = new SpendInput(btmAssetID, 9800000000L, "0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"); | |
15 | + input.setSourcePosition(2); | |
16 | + input.setSourceId("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea"); | |
17 | + input.setChange(true); | |
18 | + input.setControlProgramIndex(457); | |
19 | + input.setKeyIndex(1); | |
20 | + input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); | |
21 | + | |
22 | + Transaction tx = new Transaction.Builder() | |
23 | + .addInput(input) | |
24 | + .addOutput(new Output(btmAssetID, 8800000000L, "0014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c")) | |
25 | + .addOutput(new Output(btmAssetID, 900000000L, "00200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac66")) | |
26 | + .setTimeRange(0) | |
27 | + .build(); | |
28 | + | |
29 | + String rawTransaction = tx.rawTransaction(); | |
30 | + assert rawTransaction.equals("070100010160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302401cb779288be890a28c5209036da1a27d9fe74a51c38e0a10db4817bcf4fd05f68580239eea7dcabf19f144c77bf13d3674b5139aa51a99ba58118386c190af0e20bcbe020b05e1b7d0825953d92bf47897be08cd7751a37adb95d6a2e5224f55ab02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80b095e42001160014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c000149ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d293ad03012200200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac6600"); | |
31 | + } | |
32 | + | |
33 | + @Test | |
34 | + public void testSpendBIP32() { | |
35 | + SpendInput input = new SpendInput(btmAssetID, 11718900000L, "0014085a02ecdf934a56343aa59a3dec9d9feb86ee43"); | |
36 | + input.setSourceId("5ac79a73db78e5c9215b37cb752f0147d1157c542bb4884908ceb97abc33fe0a"); | |
37 | + input.setSourcePosition(0); | |
38 | + input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); | |
39 | + input.setChange(true); | |
40 | + input.setKeyIndex(1); | |
41 | + input.setBipProtocol(BIPProtocol.BIP32); | |
42 | + input.setControlProgramIndex(447); | |
43 | + | |
44 | + Transaction tx = new Transaction.Builder() | |
45 | + .addInput(input) | |
46 | + .addOutput(new Output(btmAssetID, 1718900000L, "001409a0961e9b592a944ca3ded0ef9403fdb25b3793")) | |
47 | + .addOutput(new Output(btmAssetID, 9900000000L, "00145ade29df622cc68d0473aa1a20fb89690451c66e")) | |
48 | + .setTimeRange(0) | |
49 | + .build(); | |
50 | + | |
51 | + String rawTx = tx.rawTransaction(); | |
52 | + assert rawTx.equals("070100010160015e5ac79a73db78e5c9215b37cb752f0147d1157c542bb4884908ceb97abc33fe0affffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0f280d42b0001160014085a02ecdf934a56343aa59a3dec9d9feb86ee43630240e37864ef905e943deb97e58b861c97f04f07c1d33b1ce4f1b6edddff0c8956a57d86fc922c45446cd38ea613fc3c9b7d5a2596f3dca7a7212f6da55b9515110420a29601468f08c57ca9c383d28736a9d5c7737cd483126d8db3d85490fe497b3502013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0aad1b3060116001409a0961e9b592a944ca3ded0ef9403fdb25b379300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8086d8f024011600145ade29df622cc68d0473aa1a20fb89690451c66e00"); | |
32 | 53 | } |
33 | 54 | |
34 | 55 | //issue asset |
35 | 56 | @Test |
36 | - public void testIssue() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
37 | - | |
38 | - String issueAssetId = "a680606d49daae62ef9cb03263ca82a0b1e3184bb6311ea52a5189207f718789"; | |
39 | - String program = "ae204cae24c2cec15491e70fc554026469496e373df9b9970b23acac8b782da0822d5151ad"; | |
40 | - String assetDefine = "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d"; | |
41 | - Transaction.AnnotatedInput input = btmUtxoToInput(); | |
42 | - Transaction Transaction = new Transaction.Builder() | |
43 | - .addInput(new Transaction.AnnotatedInput().setType(0).setAssetId(issueAssetId).setControlProgram(program).setAmount(100000000).setAssetDefinition(assetDefine).setChange(false).setKeyIndex(13)) | |
44 | -// .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"). | |
45 | -// setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0)) | |
46 | - .addInput(input.setType(1)) | |
47 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(issueAssetId).setAmount(100000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b")) | |
48 | -// .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(870000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b")) | |
49 | - .build(2000000); | |
50 | - MapTransaction.mapTx(Transaction); | |
51 | - SignTransaction sign = new SignTransaction(); | |
52 | - String rawTransaction = sign.rawTransaction(rootKey, Transaction); | |
53 | - System.out.println(rawTransaction); | |
57 | + public void testIssue() { | |
58 | + IssuanceInput issuanceInput = new IssuanceInput(); | |
59 | + issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14"); | |
60 | + issuanceInput.setAmount(100000000000L); | |
61 | + issuanceInput.setProgram("ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad"); | |
62 | + issuanceInput.setNonce("ac9d5a527f5ab00a"); | |
63 | + issuanceInput.setKeyIndex(5); | |
64 | + issuanceInput.setAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d"); | |
65 | + issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); | |
66 | + | |
67 | + SpendInput spendInput = new SpendInput(btmAssetID, 9800000000L, "0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"); | |
68 | + spendInput.setBipProtocol(BIPProtocol.BIP32); | |
69 | + spendInput.setKeyIndex(1); | |
70 | + spendInput.setChange(true); | |
71 | + spendInput.setSourceId("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea"); | |
72 | + spendInput.setSourcePosition(2); | |
73 | + spendInput.setControlProgramIndex(457); | |
74 | + spendInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); | |
75 | + | |
76 | + Transaction tx = new Transaction.Builder() | |
77 | + .addInput(issuanceInput) | |
78 | + .addInput(spendInput) | |
79 | + .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449")) | |
80 | + .addOutput(new Output(btmAssetID, 9700000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56")) | |
81 | + .setTimeRange(0) | |
82 | + .build(); | |
83 | + | |
84 | + String rawTx = tx.rawTransaction(); | |
85 | + assert rawTx.equals("0701000201300008ac9d5a527f5ab00a7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f402b001467b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d0125ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad01401ba3a3f2d3e9887e20da8d43666cc1602bb5421ffea9d2b4d7df9816fc174a43b25fb00db4775b10b679a8b5f8aa28555ebb8fb49f6275d43283daf8f5ac340d0160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e630240341c875fc815dc13ad811c9aa7c7af28a5c78765f77fd5a36dac263278fd605ae0553e34a1e645148370b7b397142ddbcd67ba33483279ab9894169b51e4f101201381d35e235813ad1e62f9a602c82abee90565639cc4573568206b55bcd2aed902013e7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f4020116001437e1aec83a4e6587ca9609e4e5aa728db700744900013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8082a99124011600148be1104e04734e5edaba5eea2e85793896b77c5600"); | |
54 | 86 | } |
55 | 87 | |
56 | 88 | |
57 | 89 | //retire asset |
58 | 90 | @Test |
59 | - public void testRetire() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
91 | + public void testRetire() { | |
60 | 92 | String arbitrary = "77656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c"; |
61 | 93 | String retireControlProgram = "6a"+Integer.toString(Hex.decode(arbitrary).length,16)+arbitrary; |
62 | 94 | String assetId1 = "207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf"; |
95 | + | |
96 | + SpendInput input1 = new SpendInput(btmAssetID, 289100000, "0014f1dc52048f439ac7fd74f8106a21da78f00de48f"); | |
97 | + input1.setRootPrivateKey(rootKey); | |
98 | + input1.setChange(true); | |
99 | + input1.setControlProgramIndex(41); | |
100 | + input1.setSourceId("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65"); | |
101 | + input1.setSourcePosition(0); | |
102 | + | |
103 | + SpendInput input2 = new SpendInput(assetId1, 70000000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf"); | |
104 | + input2.setRootPrivateKey(rootKey); | |
105 | + input2.setChange(true); | |
106 | + input2.setControlProgramIndex(26); | |
107 | + input2.setSourceId("be0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b"); | |
108 | + input2.setSourcePosition(1); | |
109 | + | |
110 | + Output output1 = new Output(btmAssetID, 279100000, "001414d362694eacfa110dc20dec77d610d22340f95b"); | |
111 | + Output output2 = new Output(assetId1, 10000000000L, retireControlProgram); | |
112 | + Output output3 = new Output(assetId1, 60000000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf"); | |
113 | + | |
63 | 114 | Transaction transaction = new Transaction.Builder() |
64 | - .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(289100000).setControlProgram("0014f1dc52048f439ac7fd74f8106a21da78f00de48f"). | |
65 | - setChange(true).setControlProgramIndex(41).setSourceId("0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65").setSourcePosition(0)) | |
66 | - .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(assetId1).setAmount(70000000000l).setControlProgram("0014bb8a039726df1b649738e9973db14a4b4fd4becf"). | |
67 | - setChange(true).setControlProgramIndex(26).setSourceId("be0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b").setSourcePosition(1)) | |
68 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(279100000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b")) | |
69 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(assetId1).setAmount(10000000000l).setControlProgram(retireControlProgram)) | |
70 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(assetId1).setAmount(60000000000l).setControlProgram("0014bb8a039726df1b649738e9973db14a4b4fd4becf")) | |
71 | - .build(2000000); | |
72 | - MapTransaction.mapTx(transaction); | |
73 | - SignTransaction sign = new SignTransaction(); | |
74 | - String rawTransaction = sign.rawTransaction(rootKey, transaction); | |
75 | - System.out.println(rawTransaction); | |
115 | + .addInput(input1) | |
116 | + .addInput(input2) | |
117 | + .addOutput(output1) | |
118 | + .addOutput(output2) | |
119 | + .addOutput(output3) | |
120 | + .setTimeRange(2000000) | |
121 | + .build(); | |
122 | + | |
123 | + String rawTransaction = transaction.rawTransaction(); | |
124 | + assert rawTransaction.equals("070180897a020160015e0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0a1ed89010001160014f1dc52048f439ac7fd74f8106a21da78f00de48f6302401660121218ab96d9f22cce712541ca34c53f4da40450669854341ca9624ad1cf10d1bfc96449fad5406224afd253ccfbdeab683f7ec7f9ee8f45e47a0c58500f2031ecc1bdd5fb9b40016358340b87646ea39faf55c0c105205cfdfdc6184725f40161015fbe0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80f8cce284020101160014bb8a039726df1b649738e9973db14a4b4fd4becf630240d7b7f1c2ca1048fd6798234f2a1e895762f83e802507a008eff52605611b67390a74eaf228b76f5589ff109b2c20eaa65fad6de2e5ab8a25b54267b607df970b20a71547e1064b5edaad92cdce6b0ace832836ba28fdeaf0b83010bed247fe927c03013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f48a85010116001414d362694eacfa110dc20dec77d610d22340f95b00014b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80c8afa02501246a2277656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c00013e207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80b09dc2df0101160014bb8a039726df1b649738e9973db14a4b4fd4becf00"); | |
76 | 125 | } |
77 | 126 | |
78 | 127 | |
79 | 128 | //utxo |
80 | - private Transaction.AnnotatedInput btmUtxoToInput() { | |
129 | + private SpendInput btmUtxoToInput() { | |
81 | 130 | String utxoJson = "\n" + |
82 | 131 | "{\n" + |
83 | 132 | " \"id\": \"687e3c3ca1ee8139e57f43697db6aaeac95b10c75b828ef2fad30abe7d047e6a\",\n" + |
@@ -97,63 +146,6 @@ public class AppTest { | ||
97 | 146 | " \"derive_rule\": 0\n" + |
98 | 147 | "}"; |
99 | 148 | UTXO utxo = UTXO.fromJson(utxoJson); |
100 | - Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo); | |
101 | - return input; | |
102 | - } | |
103 | - | |
104 | - private Transaction.AnnotatedInput otherAssetUtxoToInput() { | |
105 | - String utxoJson = "\n" + | |
106 | - "{\n" + | |
107 | - " \"id\": \"a1b729714a9ad74108999b6d2f3628c6eea2bd41d7132fca93f56c3f7c12904e\",\n" + | |
108 | - " \"amount\": 89900000000,\n" + | |
109 | - " \"address\": \"tm1qstadkjjxp7nn3eegdxc38nvur82z37ek2pq2ul\",\n" + | |
110 | - " \"program\": \"001482fadb4a460fa738e72869b113cd9c19d428fb36\",\n" + | |
111 | - " \"change\": true,\n" + | |
112 | - " \"highest\": 139905,\n" + | |
113 | - " \"account_alias\": \"wyjbtm\",\n" + | |
114 | - " \"asset_id\": \"207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf\",\n" + | |
115 | - " \"asset_alias\": \"TEST3\",\n" + | |
116 | - " \"account_id\": \"0NNSS39M00A02\",\n" + | |
117 | - " \"control_program_index\": 32,\n" + | |
118 | - " \"source_id\": \"2886e635442f2f003e1d211f3264520ffb5239944d718bc834925a0cc0798980\",\n" + | |
119 | - " \"source_pos\": 1,\n" + | |
120 | - " \"valid_height\": 0,\n" + | |
121 | - " \"derive_rule\": 0\n" + | |
122 | - "}"; | |
123 | - UTXO utxo = UTXO.fromJson(utxoJson); | |
124 | - Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo); | |
125 | - return input; | |
126 | - } | |
127 | - | |
128 | - // submit rawTransaction | |
129 | - @Test | |
130 | - public void SubmitTransaction() throws BytomException { | |
131 | - Client client = TestUtil.generateClient(); | |
132 | - String raw = "070180897a020160015e0b2cff11d1d056d95237a5f2d06059e5395e86f60e69c1e8201ea624911c0c65ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0a1ed89010001160014f1dc52048f439ac7fd74f8106a21da78f00de48f6302401660121218ab96d9f22cce712541ca34c53f4da40450669854341ca9624ad1cf10d1bfc96449fad5406224afd253ccfbdeab683f7ec7f9ee8f45e47a0c58500f2031ecc1bdd5fb9b40016358340b87646ea39faf55c0c105205cfdfdc6184725f40161015fbe0ac837e832c34a02968e54dab4f95cbeceb9fb01cd378310f6ea32219ee29b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80f8cce284020101160014bb8a039726df1b649738e9973db14a4b4fd4becf630240d7b7f1c2ca1048fd6798234f2a1e895762f83e802507a008eff52605611b67390a74eaf228b76f5589ff109b2c20eaa65fad6de2e5ab8a25b54267b607df970b20a71547e1064b5edaad92cdce6b0ace832836ba28fdeaf0b83010bed247fe927c03013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f48a85010116001414d362694eacfa110dc20dec77d610d22340f95b00014b207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80c8afa02501246a2277656c636f6d65efbc8ce6aca2e8bf8ee69da5e588b0e58e9fe5ad90e4b896e7958c00013e207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf80b09dc2df0101160014bb8a039726df1b649738e9973db14a4b4fd4becf00"; | |
133 | - SubmitTransaction.SubmitResponse submitResponse = SubmitTransaction.submitRawTransaction(client, raw); | |
134 | - System.out.println(submitResponse.tx_id); | |
135 | - } | |
136 | - | |
137 | - | |
138 | - //单输入多签 | |
139 | - @Test | |
140 | - public void testMutiSpend(){ | |
141 | - Transaction.AnnotatedInput input = btmUtxoToInput(); | |
142 | - Transaction Transaction = new Transaction.Builder() | |
143 | -// .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"). | |
144 | -// setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0)) | |
145 | - .addInput(input.setType(1)) | |
146 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(80000000).setControlProgram("00204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b17813")) | |
147 | - .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b")) | |
148 | - .build(200000); | |
149 | - Transaction transaction = MapTransaction.mapTx(Transaction); | |
150 | - SignTransaction signTransaction = new SignTransaction(); | |
151 | - String[] rootKeys = new String[3]; | |
152 | - rootKeys[0] = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; | |
153 | - rootKeys[1] = "50a23bf6200b8a98afc049a7d0296a619e2ee27fa0d6d4d271ca244b280b324347627e543cc079614642c7b88c78ce38092430b01d124663e8b84026aefefde1"; | |
154 | - rootKeys[2] = "00e4bf1251fb5aa37aa2a11dec6c0db5cec3f17aa312dbddb30e06957a32ae503ebcdfd4ad5e29be21ee9ec336e939eb72439cf6d99c785268c8f3d71c1be877"; | |
155 | - signTransaction.buildWitness(transaction, 0, rootKeys); | |
156 | - String raw = signTransaction.serializeTransaction(transaction); | |
157 | - System.out.println(raw); | |
149 | + return utxo.toSpendAnnotatedInput(); | |
158 | 150 | } |
159 | 151 | } |
@@ -1,8 +1,5 @@ | ||
1 | 1 | package io.bytom.api; |
2 | 2 | |
3 | -import io.bytom.common.DerivePrivateKey; | |
4 | -import io.bytom.common.DeriveXpub; | |
5 | -import io.bytom.common.ExpandedPrivateKey; | |
6 | 3 | import org.bouncycastle.util.encoders.Hex; |
7 | 4 | import org.junit.Test; |
8 | 5 |
@@ -18,7 +15,7 @@ public class SignerTest { | ||
18 | 15 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954"; |
19 | 16 | String expandedXprv = "20849f14bbe212d1b8917da2e1eda9afc4c21e9dd0a47f1e169a3326d45ae443236f54b987369e86ed78eb2b0a2def89a69ec69ca1059e2efe045796dc583d91"; |
20 | 17 | String hashedMessage = "99ab9ebdba106466371467b036d56a0e54ad2a6035e365a6103ba97ab553fd52"; |
21 | - byte[] sig = Signer.Ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage)); | |
18 | + byte[] sig = Signer.ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage)); | |
22 | 19 | System.out.println("sig:" + Hex.toHexString(sig)); |
23 | 20 | //expected: e628e980c690d9ef4ca8a2edee1654a6b401edc4f1af7bda3ffd97fe412522c3bab671dd4e51d0aeeb64f8d761fbdb03e296ab0c1dcbed4eafa504f412a98100 |
24 | 21 | } |
@@ -11,7 +11,7 @@ public class DerivePrivateKeyTest { | ||
11 | 11 | @Test |
12 | 12 | public void testBip44Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
13 | 13 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; |
14 | - byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 1, false, 2); | |
14 | + byte[] derivePrivateKey = DerivePrivateKey.bip44derivePrvKey(rootKey, 1, false, 2); | |
15 | 15 | System.out.println(Hex.toHexString(derivePrivateKey)); |
16 | 16 | //expected 48c65f40d860723e71b03988a22edc9ad00ae0deae992e79fb3b812edb5c3e43e78065bf46d0e8ad922cdae600fd2c2a6239b8f1f504f8f255460c6fcce023ff |
17 | 17 | } |
@@ -19,7 +19,7 @@ public class DerivePrivateKeyTest { | ||
19 | 19 | @Test |
20 | 20 | public void testBip32Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
21 | 21 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; |
22 | - byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 2); | |
22 | + byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 2, (byte) 0); | |
23 | 23 | System.out.println(Hex.toHexString(derivePrivateKey)); |
24 | 24 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc |
25 | 25 | } |
@@ -27,7 +27,7 @@ public class DerivePrivateKeyTest { | ||
27 | 27 | @Test |
28 | 28 | public void testBip32PublicKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
29 | 29 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; |
30 | - byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 14); | |
30 | + byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 14, (byte) 0); | |
31 | 31 | byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey); |
32 | 32 | System.out.println(Hex.toHexString(deriveXpub).substring(0, 64)); |
33 | 33 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc |
@@ -13,7 +13,7 @@ public class ExpandedPrivateKeyTest { | ||
13 | 13 | @Test |
14 | 14 | public void testExpandedKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { |
15 | 15 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954"; |
16 | - byte[] z = ExpandedPrivateKey.ExpandedPrivateKey(Hex.decode(childXprv)); | |
16 | + byte[] z = ExpandedPrivateKey.expandedPrivateKey(Hex.decode(childXprv)); | |
17 | 17 | System.out.println(Hex.toHexString(z)); |
18 | 18 | //expect: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3 |
19 | 19 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3 |
@@ -24,9 +24,8 @@ public class NonHardenedChildTest { | ||
24 | 24 | for (int i = 0; i < hpaths.length; i++) { |
25 | 25 | byte[] xpub = DeriveXpub.deriveXpub(res); |
26 | 26 | // System.out.println("xpub: "+Hex.toHexString(xpub)); |
27 | - res = NonHardenedChild.NHchild(paths[i], res, xpub); | |
27 | + res = NonHardenedChild.nhChild(paths[i], res, xpub); | |
28 | 28 | } |
29 | - System.out.println("res: " + Hex.toHexString(res)); | |
30 | 29 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 |
31 | 30 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 |
32 | 31 | } |
@@ -36,7 +35,6 @@ public class NonHardenedChildTest { | ||
36 | 35 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"; |
37 | 36 | String[] hpaths = {"010400000000000000", "0100000000000000"}; |
38 | 37 | byte[] childXprv = NonHardenedChild.child(Hex.decode(hxprv), hpaths); |
39 | - System.out.println("childXprv: " + Hex.toHexString(childXprv)); | |
40 | 38 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 |
41 | 39 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 |
42 | 40 | } |