-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathDeserializer.java
More file actions
138 lines (114 loc) · 4.99 KB
/
Deserializer.java
File metadata and controls
138 lines (114 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package org.arkecosystem.crypto.transactions;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import org.arkecosystem.crypto.configuration.Network;
import org.arkecosystem.crypto.encoding.Hex;
import org.arkecosystem.crypto.enums.AbiFunction;
import org.arkecosystem.crypto.transactions.types.*;
import org.arkecosystem.crypto.utils.AbiDecoder;
import org.arkecosystem.crypto.utils.RlpDecoder;
public class Deserializer {
private final byte[] rawBytes;
public Deserializer(String serialized) {
this.rawBytes = Hex.decode(serialized);
}
public static Deserializer newDeserializer(String serialized) {
return new Deserializer(serialized);
}
public AbstractTransaction deserialize() {
List<byte[]> fields = RlpDecoder.decode(rawBytes);
// Fields: [nonce, gasPrice, gasLimit, to, value, data, v, r, s]
long nonce = bytesToLong(fields.get(0));
long gasPrice = bytesToLong(fields.get(1));
long gasLimit = bytesToLong(fields.get(2));
String recipientAddress = fields.get(3).length > 0 ? "0x" + Hex.encode(fields.get(3)) : "";
String value = fields.get(4).length > 0 ? new BigInteger(1, fields.get(4)).toString() : "0";
String data = fields.get(5).length > 0 ? Hex.encode(fields.get(5)) : "";
// Recover signature
String signature = null;
if (fields.size() >= 9) {
int vEncoded =
fields.get(6).length > 0 ? new BigInteger(1, fields.get(6)).intValue() : 0;
byte[] r = fields.get(7);
byte[] s = fields.get(8);
int chainId = Network.get().chainId();
int v = vEncoded - (chainId * 2 + 35);
if (r.length > 0 || s.length > 0) {
byte[] rPadded = padTo32(r);
byte[] sPadded = padTo32(s);
byte[] sigBytes = new byte[65];
System.arraycopy(rPadded, 0, sigBytes, 0, 32);
System.arraycopy(sPadded, 0, sigBytes, 32, 32);
sigBytes[64] = (byte) v;
signature = Hex.encode(sigBytes);
}
}
// Create temp transaction to guess type
AbstractTransaction tempTransaction = new EvmCall();
tempTransaction.nonce = nonce;
tempTransaction.gasPrice = gasPrice;
tempTransaction.gasLimit = gasLimit;
tempTransaction.recipientAddress = recipientAddress;
tempTransaction.value = value;
tempTransaction.data = data;
tempTransaction.network = Network.get().version();
AbstractTransaction transaction = guessTransactionFromTransactionData(tempTransaction);
transaction.nonce = nonce;
transaction.gasPrice = gasPrice;
transaction.gasLimit = gasLimit;
transaction.recipientAddress = recipientAddress;
transaction.value = value;
transaction.data = data;
transaction.network = Network.get().version();
transaction.signature = signature;
if (signature != null) {
transaction.recoverSender();
}
transaction.computeId();
return transaction;
}
private AbstractTransaction guessTransactionFromTransactionData(
AbstractTransaction transactionData) {
if (!"0".equals(transactionData.value) && !"".equals(transactionData.value)) {
return new Transfer();
}
Map<String, Object> payloadData = decodePayload(transactionData);
if (payloadData == null) {
return new EvmCall();
}
String functionName = (String) payloadData.get("functionName");
if (functionName.equals(AbiFunction.VOTE.toString())) {
return new Vote(transactionData.toHashMap());
} else if (functionName.equals(AbiFunction.UNVOTE.toString())) {
return new Unvote(transactionData.toHashMap());
} else if (functionName.equals(AbiFunction.VALIDATOR_REGISTRATION.toString())) {
return new ValidatorRegistration(transactionData.toHashMap());
} else if (functionName.equals(AbiFunction.VALIDATOR_RESIGNATION.toString())) {
return new ValidatorResignation(transactionData.toHashMap());
}
return new EvmCall();
}
private Map<String, Object> decodePayload(AbstractTransaction transaction) {
String payload = transaction.data != null ? transaction.data : "";
if (payload.isEmpty()) {
return null;
}
try {
AbiDecoder abiDecoder = new AbiDecoder();
return abiDecoder.decodeFunctionData(payload);
} catch (Exception e) {
return null;
}
}
private static long bytesToLong(byte[] bytes) {
if (bytes.length == 0) return 0;
return new BigInteger(1, bytes).longValue();
}
private static byte[] padTo32(byte[] input) {
if (input.length >= 32) return input;
byte[] padded = new byte[32];
System.arraycopy(input, 0, padded, 32 - input.length, input.length);
return padded;
}
}