Skip to content

Commit 123512b

Browse files
authored
Merge pull request #1 from shenao78/features-signTx
add trandaction bc layer
2 parents 052d656 + 3646f25 commit 123512b

File tree

15 files changed

+616
-11
lines changed

15 files changed

+616
-11
lines changed

tx-signer/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@
3636
<groupId>com.google.crypto.tink</groupId>
3737
<artifactId>tink</artifactId>
3838
<version>1.1.1</version>
39+
<exclusions>
40+
<exclusion>
41+
<groupId>com.google.protobuf</groupId>
42+
<artifactId>protobuf-java</artifactId>
43+
</exclusion>
44+
</exclusions>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>com.google.protobuf</groupId>
49+
<artifactId>protobuf-java</artifactId>
50+
<version>3.6.1</version>
3951
</dependency>
4052
<!-- sha3-256 -->
4153
<dependency>

tx-signer/src/main/java/io/bytom/api/SignTransactionImpl.java

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
import io.bytom.common.Constants;
44
import io.bytom.common.ExpandedPrivateKey;
5+
import io.bytom.types.*;
56
import org.bouncycastle.jcajce.provider.digest.SHA3;
67
import org.bouncycastle.util.encoders.Hex;
78

89
import java.io.ByteArrayOutputStream;
910
import java.io.IOException;
1011
import java.math.BigInteger;
11-
import java.util.Arrays;
12+
import java.util.*;
1213

13-
import org.bouncycastle.util.encoders.Hex;
1414

1515
/**
1616
* Created by liqiang on 2018/10/24.
@@ -21,7 +21,7 @@ public String signTransaction(SignTransaction tx, BigInteger[] keys) {
2121

2222
String txSign = null;
2323
//组装计算program、inputID、sourceID(muxID)、txID, json数据中这些字段的值为测试值,需重新计算
24-
buildData(tx);
24+
mapTransaction(tx);
2525

2626
//签名得到signatures
2727
generateSignatures(tx,keys);
@@ -136,14 +136,76 @@ public byte[] hashFn(byte[] hashedInputHex, byte[] txID) {
136136
return digest256.digest(data);
137137
}
138138

139-
public SignTransaction buildData(SignTransaction signTransaction) {
140-
//build program by address
139+
public void mapTransaction(SignTransaction signTransaction) {
140+
Map<Hash, Entry> entryMap = new HashMap<>();
141+
ValueSource[] muxSources = new ValueSource[signTransaction.inputs.size()];
142+
List<Spend> spends = new ArrayList<>();
141143

142-
//build sourceId(muxId), inputId, txId
144+
try {
145+
for (int i = 0; i < signTransaction.inputs.size(); i++) {
146+
SignTransaction.AnnotatedInput input = signTransaction.inputs.get(i);
147+
Program proc = new Program(1, Hex.decode(input.controlProgram));
143148

144-
return signTransaction;
149+
AssetID assetID = new AssetID(input.assetId);
150+
AssetAmount assetAmount = new AssetAmount(assetID, input.amount);
151+
152+
Hash sourceID = new Hash(input.sourceId);
153+
ValueSource src = new ValueSource(sourceID, assetAmount, input.sourcePosition);
154+
Output prevout = new Output(src, proc, 0);
155+
Hash prevoutID = addEntry(entryMap, prevout);
156+
157+
input.spentOutputId = prevoutID.toString();
158+
159+
Spend spend = new Spend(prevoutID, i);
160+
Hash spendID = addEntry(entryMap, spend);
161+
162+
input.inputID = spendID.toString();
163+
164+
muxSources[i] = new ValueSource(spendID, assetAmount, 0);
165+
spends.add(spend);
166+
}
167+
168+
Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));
169+
Hash muxID = addEntry(entryMap, mux);
170+
171+
for (Spend spend : spends) {
172+
Output spendOutput = (Output) entryMap.get(spend.spentOutputID);
173+
spend.setDestination(muxID, spendOutput.source.value, spend.ordinal);
174+
}
175+
176+
List<Hash> resultIDList = new ArrayList<>();
177+
for (int i = 0; i < signTransaction.outputs.size(); i++) {
178+
SignTransaction.AnnotatedOutput output = signTransaction.outputs.get(i);
179+
180+
AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount);
181+
ValueSource src = new ValueSource(muxID, amount, i);
182+
Program prog = new Program(1, Hex.decode(output.controlProgram));
183+
Output oup = new Output(src, prog, i);
184+
185+
Hash resultID = addEntry(entryMap, oup);
186+
resultIDList.add(resultID);
187+
188+
output.id = resultID.toString();
189+
190+
ValueDestination destination = new ValueDestination(resultID, src.value, 0);
191+
mux.witnessDestinations.add(destination);
192+
}
193+
194+
TxHeader txHeader = new TxHeader(signTransaction.version, signTransaction.size, signTransaction.timeRange, resultIDList.toArray(new Hash[]{}));
195+
Hash txID = addEntry(entryMap, txHeader);
196+
signTransaction.txID = txID.toString();
197+
} catch (Exception e) {
198+
throw new RuntimeException(e);
199+
}
145200
}
146201

202+
private Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {
203+
Hash id = entry.entryID();
204+
entryMap.put(id, entry);
205+
return id;
206+
}
207+
208+
147209
public SignTransaction generateSignatures(SignTransaction signTransaction, BigInteger[] keys) {
148210
SignTransaction.AnnotatedInput input = signTransaction.inputs.get(0);
149211
input.witnessComponent.signatures = new String[keys.length];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.bytom.types;
2+
3+
public class AssetAmount {
4+
5+
public AssetID assetID;
6+
7+
public long amount;
8+
9+
public AssetAmount() {}
10+
11+
public AssetAmount(AssetID assetID, long amount) {
12+
this.assetID = assetID;
13+
this.amount = amount;
14+
}
15+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.bytom.types;
2+
3+
import org.bouncycastle.util.encoders.Hex;
4+
5+
import java.util.Objects;
6+
7+
public class AssetID {
8+
9+
private String hexValue;
10+
11+
public AssetID() {}
12+
13+
public AssetID(String hexValue) {
14+
this.hexValue = hexValue;
15+
}
16+
17+
public AssetID(byte[] byteArray) {
18+
this.hexValue = Hex.toHexString(byteArray);
19+
}
20+
21+
public byte[] toByteArray() {
22+
return Hex.decode(this.hexValue);
23+
}
24+
25+
@Override
26+
public boolean equals(Object o) {
27+
if (this == o) return true;
28+
if (o == null || getClass() != o.getClass()) return false;
29+
AssetID assetID = (AssetID) o;
30+
return Objects.equals(hexValue, assetID.hexValue);
31+
}
32+
33+
@Override
34+
public int hashCode() {
35+
return Objects.hash(hexValue);
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return this.hexValue;
41+
}
42+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.bytom.types;
2+
3+
import io.bytom.util.OutputUtil;
4+
import org.bouncycastle.jcajce.provider.digest.SHA3;
5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.lang.reflect.Field;
8+
9+
public abstract class Entry {
10+
11+
public abstract String typ();
12+
13+
public abstract void writeForHash(ByteArrayOutputStream out);
14+
15+
public void mustWriteForHash(ByteArrayOutputStream out, Object data) {
16+
try {
17+
if (data == null) {
18+
return;
19+
}
20+
if (data instanceof Byte) {
21+
OutputUtil.writeByte(out, (byte) data);
22+
} else if (data instanceof Long) {
23+
OutputUtil.writeLong(out, (long) data);
24+
} else if (data instanceof byte[]) {
25+
OutputUtil.writeVarstr(out, (byte[]) data);
26+
} else if (data instanceof byte[][]) {
27+
OutputUtil.writeVarstrList(out, (byte[][]) data);
28+
} else if (data instanceof String) {
29+
OutputUtil.writeVarstr(out, ((String) data).getBytes());
30+
} else if (data instanceof Hash) {
31+
out.write(((Hash) data).toByteArray());
32+
} else if (data instanceof AssetID) {
33+
out.write(((AssetID) data).toByteArray());
34+
} else if (data.getClass().isArray()) {
35+
Object[] array = (Object[]) data;
36+
OutputUtil.writeVarint(out, array.length);
37+
for (Object obj : array) {
38+
mustWriteForHash(out, obj);
39+
}
40+
} else {
41+
Class<?> cls = data.getClass();
42+
Field[] fields = cls.getFields();
43+
for (Field field : fields) {
44+
mustWriteForHash(out, field.get(data));
45+
}
46+
}
47+
} catch (Exception e) {
48+
throw new RuntimeException(e);
49+
}
50+
}
51+
52+
public Hash entryID() {
53+
SHA3.Digest256 digest256 = new SHA3.Digest256();
54+
ByteArrayOutputStream hasher = new ByteArrayOutputStream();
55+
ByteArrayOutputStream bh = new ByteArrayOutputStream();
56+
try {
57+
hasher.write("entryid:".getBytes());
58+
hasher.write(this.typ().getBytes());
59+
hasher.write(":".getBytes());
60+
61+
this.writeForHash(bh);
62+
hasher.write(digest256.digest(bh.toByteArray()));
63+
64+
return new Hash(digest256.digest(hasher.toByteArray()));
65+
} catch (IOException e) {
66+
throw new RuntimeException(e);
67+
} finally {
68+
try {
69+
bh.close();
70+
hasher.close();
71+
} catch (IOException e) {
72+
e.printStackTrace();
73+
}
74+
}
75+
}
76+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.bytom.types;
2+
3+
import org.bouncycastle.util.encoders.Hex;
4+
5+
import java.util.Objects;
6+
7+
public class Hash {
8+
9+
private String hexValue;
10+
11+
public Hash() {}
12+
13+
public Hash(String hexValue) {
14+
this.hexValue = hexValue;
15+
}
16+
17+
public Hash(byte[] byteArray) {
18+
this.hexValue = Hex.toHexString(byteArray);
19+
}
20+
21+
public byte[] toByteArray() {
22+
return Hex.decode(this.hexValue);
23+
}
24+
25+
@Override
26+
public boolean equals(Object o) {
27+
if (this == o) return true;
28+
if (o == null || getClass() != o.getClass()) return false;
29+
Hash hash = (Hash) o;
30+
return Objects.equals(hexValue, hash.hexValue);
31+
}
32+
33+
@Override
34+
public int hashCode() {
35+
return Objects.hash(hexValue);
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return this.hexValue;
41+
}
42+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.bytom.types;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
public class Mux extends Entry {
8+
9+
public ValueSource[] sources;
10+
11+
public Program program;
12+
13+
public List<ValueDestination> witnessDestinations = new ArrayList<>();
14+
15+
public Mux() {}
16+
17+
public Mux(ValueSource[] sources, Program program) {
18+
this();
19+
this.sources = sources;
20+
this.program = program;
21+
}
22+
23+
@Override
24+
public String typ() {
25+
return "mux1";
26+
}
27+
28+
@Override
29+
public void writeForHash(ByteArrayOutputStream out) {
30+
mustWriteForHash(out, this.sources);
31+
mustWriteForHash(out, this.program);
32+
}
33+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.bytom.types;
2+
3+
import java.io.ByteArrayOutputStream;
4+
5+
public class Output extends Entry {
6+
7+
public ValueSource source;
8+
9+
public Program controlProgram;
10+
11+
public Integer ordinal;
12+
13+
public Output() {
14+
this.source = new ValueSource();
15+
this.controlProgram = new Program();
16+
}
17+
18+
19+
public Output(ValueSource source, Program controlProgram, Integer ordinal) {
20+
this.source = source;
21+
this.controlProgram = controlProgram;
22+
this.ordinal = ordinal;
23+
}
24+
25+
@Override
26+
public String typ() {
27+
return "output1";
28+
}
29+
30+
@Override
31+
public void writeForHash(ByteArrayOutputStream out) {
32+
mustWriteForHash(out, this.source);
33+
mustWriteForHash(out, this.controlProgram);
34+
}
35+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.bytom.types;
2+
3+
4+
public class Program {
5+
6+
public long vmVersion;
7+
8+
public byte[] code;
9+
10+
public Program() {}
11+
12+
public Program(long vmVersion, byte[] code) {
13+
this.vmVersion = vmVersion;
14+
this.code = code;
15+
}
16+
}

0 commit comments

Comments
 (0)