Coverage Summary for Class: BridgeSerializationUtils (co.rsk.peg)
Class |
Class, %
|
Method, %
|
Line, %
|
BridgeSerializationUtils |
0%
(0/1)
|
0%
(0/69)
|
0%
(0/381)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.peg;
20
21 import co.rsk.bitcoinj.core.*;
22 import co.rsk.bitcoinj.script.Script;
23 import co.rsk.config.BridgeConstants;
24 import co.rsk.core.RskAddress;
25 import co.rsk.crypto.Keccak256;
26 import co.rsk.peg.bitcoin.CoinbaseInformation;
27 import co.rsk.peg.fastbridge.FastBridgeFederationInformation;
28 import co.rsk.peg.whitelist.OneOffWhiteListEntry;
29 import co.rsk.peg.whitelist.UnlimitedWhiteListEntry;
30 import org.apache.commons.lang3.tuple.Pair;
31 import org.bouncycastle.util.BigIntegers;
32 import org.ethereum.crypto.ECKey;
33 import org.ethereum.util.RLP;
34 import org.ethereum.util.RLPElement;
35 import org.ethereum.util.RLPList;
36
37 import javax.annotation.Nullable;
38 import java.io.ByteArrayInputStream;
39 import java.io.ByteArrayOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.math.BigInteger;
43 import java.nio.charset.StandardCharsets;
44 import java.time.Instant;
45 import java.util.*;
46 import java.util.stream.Collectors;
47
48 /**
49 * Created by mario on 20/04/17.
50 */
51 public class BridgeSerializationUtils {
52 private static final int FEDERATION_RLP_LIST_SIZE = 3;
53 private static final int FEDERATION_CREATION_TIME_INDEX = 0;
54 private static final int FEDERATION_CREATION_BLOCK_NUMBER_INDEX = 1;
55 private static final int FEDERATION_MEMBERS_INDEX = 2;
56
57 private static final int FEDERATION_MEMBER_LIST_SIZE = 3;
58 private static final int FEDERATION_MEMBER_BTC_KEY_INDEX = 0;
59 private static final int FEDERATION_MEMBER_RSK_KEY_INDEX = 1;
60 private static final int FEDERATION_MEMBER_MST_KEY_INDEX = 2;
61
62
63 private BridgeSerializationUtils(){}
64
65 public static byte[] serializeMap(SortedMap<Keccak256, BtcTransaction> map) {
66 int ntxs = map.size();
67
68 byte[][] bytes = new byte[ntxs * 2][];
69 int n = 0;
70
71 for (Map.Entry<Keccak256, BtcTransaction> entry : map.entrySet()) {
72 bytes[n++] = RLP.encodeElement(entry.getKey().getBytes());
73 bytes[n++] = RLP.encodeElement(entry.getValue().bitcoinSerialize());
74 }
75
76 return RLP.encodeList(bytes);
77 }
78
79 public static SortedMap<Keccak256, BtcTransaction> deserializeMap(byte[] data, NetworkParameters networkParameters, boolean noInputsTxs) {
80 SortedMap<Keccak256, BtcTransaction> map = new TreeMap<>();
81
82 if (data == null || data.length == 0) {
83 return map;
84 }
85
86 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
87
88 int ntxs = rlpList.size() / 2;
89
90 for (int k = 0; k < ntxs; k++) {
91 Keccak256 hash = new Keccak256(rlpList.get(k * 2).getRLPData());
92 byte[] payload = rlpList.get(k * 2 + 1).getRLPData();
93 BtcTransaction tx;
94 if (!noInputsTxs) {
95 tx = new BtcTransaction(networkParameters, payload);
96 } else {
97 tx = new BtcTransaction(networkParameters);
98 tx.parseNoInputs(payload);
99 }
100 map.put(hash, tx);
101 }
102
103 return map;
104 }
105
106 public static byte[] serializeUTXOList(List<UTXO> list) throws IOException {
107 int nutxos = list.size();
108
109 byte[][] bytes = new byte[nutxos][];
110 int n = 0;
111
112 for (UTXO utxo : list) {
113 try (ByteArrayOutputStream ostream = new ByteArrayOutputStream()) {
114 utxo.serializeToStream(ostream);
115 bytes[n++] = RLP.encodeElement(ostream.toByteArray());
116 }
117 }
118
119 return RLP.encodeList(bytes);
120 }
121
122 public static List<UTXO> deserializeUTXOList(byte[] data) throws IOException {
123 List<UTXO> list = new ArrayList<>();
124
125 if (data == null || data.length == 0) {
126 return list;
127 }
128
129 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
130
131 int nutxos = rlpList.size();
132
133 for (int k = 0; k < nutxos; k++) {
134 byte[] utxoBytes = rlpList.get(k).getRLPData();
135 InputStream istream = new ByteArrayInputStream(utxoBytes);
136 UTXO utxo = new UTXO(istream);
137 list.add(utxo);
138 }
139
140 return list;
141 }
142
143 public static byte[] serializeSet(SortedSet<Sha256Hash> set) {
144 int nhashes = set.size();
145
146 byte[][] bytes = new byte[nhashes][];
147 int n = 0;
148
149 for (Sha256Hash hash : set) {
150 bytes[n++] = RLP.encodeElement(hash.getBytes());
151 }
152
153 return RLP.encodeList(bytes);
154 }
155
156 public static SortedSet<Sha256Hash> deserializeSet(byte[] data) {
157 SortedSet<Sha256Hash> set = new TreeSet<>();
158
159 if (data == null || data.length == 0) {
160 return set;
161 }
162
163 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
164
165 int nhashes = rlpList.size();
166
167 for (int k = 0; k < nhashes; k++) {
168 set.add(Sha256Hash.wrap(rlpList.get(k).getRLPData()));
169 }
170
171 return set;
172 }
173
174 public static byte[] serializeMapOfHashesToLong(Map<Sha256Hash, Long> map) {
175 byte[][] bytes = new byte[map.size() * 2][];
176 int n = 0;
177
178 List<Sha256Hash> sortedHashes = new ArrayList<>(map.keySet());
179 Collections.sort(sortedHashes);
180
181 for (Sha256Hash hash : sortedHashes) {
182 Long value = map.get(hash);
183 bytes[n++] = RLP.encodeElement(hash.getBytes());
184 bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(value));
185 }
186
187 return RLP.encodeList(bytes);
188 }
189
190 public static Map<Sha256Hash, Long> deserializeMapOfHashesToLong(byte[] data) {
191 Map<Sha256Hash, Long> map = new HashMap<>();
192
193 if (data == null || data.length == 0) {
194 return map;
195 }
196
197 RLPList rlpList = (RLPList) RLP.decode2(data).get(0);
198
199 // List size must be even - key, value pairs expected in sequence
200 if (rlpList.size() % 2 != 0) {
201 throw new RuntimeException("deserializeMapOfHashesToLong: expected an even number of entries, but odd given");
202 }
203
204 int numEntries = rlpList.size() / 2;
205
206 for (int k = 0; k < numEntries; k++) {
207 Sha256Hash hash = Sha256Hash.wrap(rlpList.get(k * 2).getRLPData());
208 long number = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 2 + 1).getRLPData()).longValue();
209 map.put(hash, number);
210 }
211
212 return map;
213 }
214
215 private interface FederationMemberSerializer {
216 byte[] serialize(FederationMember federationMember);
217 }
218
219 private interface FederationMemberDesserializer {
220 FederationMember deserialize(byte[] data);
221 }
222
223 /**
224 * A federation is serialized as a list in the following order:
225 * - creation time
226 * - creation block number
227 * - list of federation members -> [member1, member2, ..., membern], sorted
228 * using the lexicographical order of the public keys of the members
229 * (see FederationMember.BTC_RSK_MST_PUBKEYS_COMPARATOR).
230 * Each federation member is in turn serialized using the provided FederationMemberSerializer.
231 */
232 private static byte[] serializeFederationWithSerializer(Federation federation, FederationMemberSerializer federationMemberSerializer) {
233 List<byte[]> federationMembers = federation.getMembers().stream()
234 .sorted(FederationMember.BTC_RSK_MST_PUBKEYS_COMPARATOR)
235 .map(member -> RLP.encodeElement(federationMemberSerializer.serialize(member)))
236 .collect(Collectors.toList());
237
238 byte[][] rlpElements = new byte[FEDERATION_RLP_LIST_SIZE][];
239 rlpElements[FEDERATION_CREATION_TIME_INDEX] = RLP.encodeBigInteger(BigInteger.valueOf(federation.getCreationTime().toEpochMilli()));
240 rlpElements[FEDERATION_CREATION_BLOCK_NUMBER_INDEX] = RLP.encodeBigInteger(BigInteger.valueOf(federation.getCreationBlockNumber()));
241 rlpElements[FEDERATION_MEMBERS_INDEX] = RLP.encodeList((byte[][])federationMembers.toArray(new byte[federationMembers.size()][]));
242 return RLP.encodeList(rlpElements);
243 }
244
245 // For the serialization format, see BridgeSerializationUtils::serializeFederationWithSerializer
246 private static Federation deserializeFederationWithDeserializer(
247 byte[] data,
248 NetworkParameters networkParameters,
249 FederationMemberDesserializer federationMemberDesserializer) {
250
251 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
252
253 if (rlpList.size() != FEDERATION_RLP_LIST_SIZE) {
254 throw new RuntimeException(String.format("Invalid serialized Federation. Expected %d elements but got %d", FEDERATION_RLP_LIST_SIZE, rlpList.size()));
255 }
256
257 byte[] creationTimeBytes = rlpList.get(FEDERATION_CREATION_TIME_INDEX).getRLPData();
258 Instant creationTime = Instant.ofEpochMilli(BigIntegers.fromUnsignedByteArray(creationTimeBytes).longValue());
259
260 byte[] creationBlockNumberBytes = rlpList.get(FEDERATION_CREATION_BLOCK_NUMBER_INDEX).getRLPData();
261 long creationBlockNumber = BigIntegers.fromUnsignedByteArray(creationBlockNumberBytes).longValue();
262
263 RLPList rlpMembers = (RLPList) rlpList.get(FEDERATION_MEMBERS_INDEX);
264
265 List<FederationMember> federationMembers = new ArrayList();
266
267 for (int k = 0; k < rlpMembers.size(); k++) {
268 RLPElement element = rlpMembers.get(k);
269 FederationMember member = federationMemberDesserializer.deserialize(element.getRLPData());
270 federationMembers.add(member);
271 }
272
273 return new Federation(federationMembers, creationTime, creationBlockNumber, networkParameters);
274 }
275
276 /**
277 * For the federation serialization format, see serializeFederationWithSerializer.
278 * For compatibility with blocks before the Wasabi network upgrade,
279 * each federation member is serialized only as its compressed BTC public key.
280 */
281 public static byte[] serializeFederationOnlyBtcKeys(Federation federation) {
282 return serializeFederationWithSerializer(federation,
283 federationMember -> federationMember.getBtcPublicKey().getPubKeyPoint().getEncoded(true));
284 }
285
286 // For the serialization format, see BridgeSerializationUtils::serializeFederationOnlyBtcKeys
287 public static Federation deserializeFederationOnlyBtcKeys(byte[] data, NetworkParameters networkParameters) {
288 return deserializeFederationWithDeserializer(data, networkParameters,
289 (pubKeyBytes -> FederationMember.getFederationMemberFromKey(BtcECKey.fromPublicOnly(pubKeyBytes))));
290 }
291
292 /**
293 * For the federation serialization format, see serializeFederationWithSerializer.
294 * For the federation member serialization format, see serializeFederationMember.
295 */
296 public static byte[] serializeFederation(Federation federation) {
297 return serializeFederationWithSerializer(federation,
298 BridgeSerializationUtils::serializeFederationMember);
299 }
300
301 // For the serialization format, see BridgeSerializationUtils::serializeFederation
302 public static Federation deserializeFederation(
303 byte[] data,
304 NetworkParameters networkParameters
305 ) {
306 return deserializeFederationWithDeserializer(
307 data,
308 networkParameters,
309 BridgeSerializationUtils::deserializeFederationMember
310 );
311 }
312
313 public static ErpFederation deserializeErpFederation(
314 byte[] data,
315 NetworkParameters networkParameters,
316 BridgeConstants bridgeConstants
317 ) {
318 Federation federation = deserializeFederationWithDeserializer(
319 data,
320 networkParameters,
321 BridgeSerializationUtils::deserializeFederationMember
322 );
323
324 return new ErpFederation(
325 federation.getMembers(),
326 federation.creationTime,
327 federation.getCreationBlockNumber(),
328 federation.getBtcParams(),
329 bridgeConstants.getErpFedPubKeysList(),
330 bridgeConstants.getErpFedActivationDelay()
331 );
332 }
333
334 /**
335 * A FederationMember is serialized as a list in the following order:
336 * - BTC public key
337 * - RSK public key
338 * - MST public key
339 * All keys are stored in their COMPRESSED versions.
340 */
341 public static byte[] serializeFederationMember(FederationMember federationMember) {
342 byte[][] rlpElements = new byte[FEDERATION_MEMBER_LIST_SIZE][];
343 rlpElements[FEDERATION_MEMBER_BTC_KEY_INDEX] = RLP.encodeElement(
344 federationMember.getBtcPublicKey().getPubKeyPoint().getEncoded(true)
345 );
346 rlpElements[FEDERATION_MEMBER_RSK_KEY_INDEX] = RLP.encodeElement(federationMember.getRskPublicKey().getPubKey(true));
347 rlpElements[FEDERATION_MEMBER_MST_KEY_INDEX] = RLP.encodeElement(federationMember.getMstPublicKey().getPubKey(true));
348 return RLP.encodeList(rlpElements);
349 }
350
351 // For the serialization format, see BridgeSerializationUtils::serializeFederationMember
352 private static FederationMember deserializeFederationMember(byte[] data) {
353 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
354
355 if (rlpList.size() != FEDERATION_RLP_LIST_SIZE) {
356 throw new RuntimeException(String.format("Invalid serialized FederationMember. Expected %d elements but got %d", FEDERATION_MEMBER_LIST_SIZE, rlpList.size()));
357 }
358
359 BtcECKey btcKey = BtcECKey.fromPublicOnly(rlpList.get(FEDERATION_MEMBER_BTC_KEY_INDEX).getRLPData());
360 ECKey rskKey = ECKey.fromPublicOnly(rlpList.get(FEDERATION_MEMBER_RSK_KEY_INDEX).getRLPData());
361 ECKey mstKey = ECKey.fromPublicOnly(rlpList.get(FEDERATION_MEMBER_MST_KEY_INDEX).getRLPData());
362
363 return new FederationMember(btcKey, rskKey, mstKey);
364 }
365
366 /**
367 * A pending federation is serialized as the
368 * public keys conforming it.
369 * This is a legacy format for blocks before the Wasabi
370 * network upgrade.
371 * See BridgeSerializationUtils::serializeBtcPublicKeys
372 */
373 public static byte[] serializePendingFederationOnlyBtcKeys(PendingFederation pendingFederation) {
374 return serializeBtcPublicKeys(pendingFederation.getBtcPublicKeys());
375 }
376
377 // For the serialization format, see BridgeSerializationUtils::serializePendingFederationOnlyBtcKeys
378 // and serializePublicKeys::deserializeBtcPublicKeys
379 public static PendingFederation deserializePendingFederationOnlyBtcKeys(byte[] data) {
380 // BTC, RSK and MST keys are the same
381 List<FederationMember> members = deserializeBtcPublicKeys(data).stream().map(pk ->
382 FederationMember.getFederationMemberFromKey(pk)
383 ).collect(Collectors.toList());
384
385 return new PendingFederation(members);
386 }
387
388 /**
389 * A pending federation is serialized as the
390 * list of its sorted members serialized.
391 * For the member serialization format, see BridgeSerializationUtils::serializeFederationMember
392 */
393 public static byte[] serializePendingFederation(PendingFederation pendingFederation) {
394 List<byte[]> encodedMembers = pendingFederation.getMembers().stream()
395 .sorted(FederationMember.BTC_RSK_MST_PUBKEYS_COMPARATOR)
396 .map(BridgeSerializationUtils::serializeFederationMember)
397 .collect(Collectors.toList());
398 return RLP.encodeList(encodedMembers.toArray(new byte[0][]));
399 }
400
401 // For the serialization format, see BridgeSerializationUtils::serializePendingFederation
402 public static PendingFederation deserializePendingFederation(byte[] data) {
403 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
404
405 List<FederationMember> members = new ArrayList<>();
406
407 for (int k = 0; k < rlpList.size(); k++) {
408 RLPElement element = rlpList.get(k);
409 FederationMember member = deserializeFederationMember(element.getRLPData());
410 members.add(member);
411 }
412
413 return new PendingFederation(members);
414 }
415
416 // An ABI call election is serialized as a list of the votes, like so:
417 // spec_1, voters_1, ..., spec_n, voters_n
418 // Specs are sorted by their signed byte encoding lexicographically.
419 public static byte[] serializeElection(ABICallElection election) {
420 byte[][] bytes = new byte[election.getVotes().size() * 2][];
421 int n = 0;
422
423 Map<ABICallSpec, List<RskAddress>> votes = election.getVotes();
424 ABICallSpec[] specs = votes.keySet().toArray(new ABICallSpec[0]);
425 Arrays.sort(specs, ABICallSpec.byBytesComparator);
426
427 for (ABICallSpec spec : specs) {
428 bytes[n++] = serializeABICallSpec(spec);
429 bytes[n++] = serializeVoters(votes.get(spec));
430 }
431
432 return RLP.encodeList(bytes);
433 }
434
435 // For the serialization format, see BridgeSerializationUtils::serializeElection
436 public static ABICallElection deserializeElection(byte[] data, AddressBasedAuthorizer authorizer) {
437 if (data == null || data.length == 0) {
438 return new ABICallElection(authorizer);
439 }
440
441 RLPList rlpList = (RLPList) RLP.decode2(data).get(0);
442
443 // List size must be even - key, value pairs expected in sequence
444 if (rlpList.size() % 2 != 0) {
445 throw new RuntimeException("deserializeElection: expected an even number of entries, but odd given");
446 }
447
448 int numEntries = rlpList.size() / 2;
449
450 Map<ABICallSpec, List<RskAddress>> votes = new HashMap<>();
451
452 for (int k = 0; k < numEntries; k++) {
453 ABICallSpec spec = deserializeABICallSpec(rlpList.get(k * 2).getRLPData());
454 List<RskAddress> specVotes = deserializeVoters(rlpList.get(k * 2 + 1).getRLPData());
455 votes.put(spec, specVotes);
456 }
457
458 return new ABICallElection(authorizer, votes);
459 }
460
461 /**
462 * Serializes the data stored in the Tuple.
463 * @param data data MUST be composed of a list of {@link co.rsk.peg.whitelist.OneOffWhiteListEntry} and the value of disableBlockHeight obtained from {@link co.rsk.peg.whitelist.LockWhitelist}
464 * @return the serialized data
465 */
466 public static byte[] serializeOneOffLockWhitelist(Pair<List<OneOffWhiteListEntry>, Integer> data) {
467 List<OneOffWhiteListEntry> entries = data.getLeft();
468 Integer disableBlockHeight = data.getRight();
469 int serializationSize = entries.size() * 2 + 1;
470 byte[][] serializedLockWhitelist = new byte[serializationSize][];
471 for (int i = 0; i < entries.size(); i++) {
472 OneOffWhiteListEntry entry = entries.get(i);
473 serializedLockWhitelist[2 * i] = RLP.encodeElement(entry.address().getHash160());
474 serializedLockWhitelist[2 * i + 1] = RLP.encodeBigInteger(BigInteger.valueOf(entry.maxTransferValue().longValue()));
475 }
476 serializedLockWhitelist[serializationSize - 1] = RLP.encodeBigInteger(BigInteger.valueOf(disableBlockHeight));
477 return RLP.encodeList(serializedLockWhitelist);
478 }
479
480 /**
481 * Serializes the provided list of {@link co.rsk.peg.whitelist.UnlimitedWhiteListEntry}
482 * @param entries
483 * @return the serialized data
484 */
485 public static byte[] serializeUnlimitedLockWhitelist(List<UnlimitedWhiteListEntry> entries) {
486 int serializationSize = entries.size();
487 byte[][] serializedLockWhitelist = new byte[serializationSize][];
488 for (int i = 0; i < entries.size(); i++) {
489 serializedLockWhitelist[i] = RLP.encodeElement(entries.get(i).address().getHash160());
490 }
491 return RLP.encodeList(serializedLockWhitelist);
492 }
493
494 public static Pair<HashMap<Address, OneOffWhiteListEntry>, Integer> deserializeOneOffLockWhitelistAndDisableBlockHeight(byte[] data, NetworkParameters parameters) {
495 if (data == null || data.length == 0) {
496 return null;
497 }
498 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
499 int serializedAddressesSize = rlpList.size() - 1;
500
501 // serialized addresses size must be even - key, value pairs expected in sequence
502 if (serializedAddressesSize % 2 != 0) {
503 throw new RuntimeException("deserializeLockWhitelist: expected an even number of addresses, but odd given");
504 }
505
506 HashMap<Address, OneOffWhiteListEntry> entries = new HashMap<>(serializedAddressesSize / 2);
507 for (int i = 0; i < serializedAddressesSize; i = i + 2) {
508 byte[] hash160 = rlpList.get(i).getRLPData();
509 byte[] maxTransferValueData = rlpList.get(i + 1).getRLPData();
510 Address address = new Address(parameters, hash160);
511 entries.put(address, new OneOffWhiteListEntry(address, Coin.valueOf(safeToBigInteger(maxTransferValueData).longValueExact())));
512 }
513 int disableBlockHeight = safeToBigInteger(rlpList.get(serializedAddressesSize).getRLPData()).intValueExact();
514 return Pair.of(entries, disableBlockHeight);
515 }
516
517 public static Map<Address, UnlimitedWhiteListEntry> deserializeUnlimitedLockWhitelistEntries(byte[] data, NetworkParameters parameters) {
518 if (data == null) {
519 return new HashMap<>();
520 }
521
522 RLPList unlimitedWhitelistEntriesRlpList = (RLPList)RLP.decode2(data).get(0);
523 int unlimitedWhitelistEntriesSerializedAddressesSize = unlimitedWhitelistEntriesRlpList.size();
524
525 Map<Address, UnlimitedWhiteListEntry> entries = new HashMap<>(unlimitedWhitelistEntriesSerializedAddressesSize);
526
527 for (int j = 0; j < unlimitedWhitelistEntriesSerializedAddressesSize; j++) {
528 byte[] hash160 = unlimitedWhitelistEntriesRlpList.get(j).getRLPData();
529 Address address = new Address(parameters, hash160);
530 entries.put(address, new UnlimitedWhiteListEntry(address));
531 }
532
533 return entries;
534 }
535
536 private static BigInteger safeToBigInteger(byte[] data) {
537 return data == null ? BigInteger.ZERO : BigIntegers.fromUnsignedByteArray(data);
538 }
539
540 public static byte[] serializeCoin(Coin coin) {
541 return RLP.encodeBigInteger(BigInteger.valueOf(coin.getValue()));
542 }
543
544 @Nullable
545 public static Coin deserializeCoin(byte[] data) {
546 if (data == null || data.length == 0) {
547 return null;
548 }
549
550 return Coin.valueOf(RLP.decodeBigInteger(data, 0).longValueExact());
551 }
552
553 // A ReleaseRequestQueue is serialized as follows:
554 // [address_1, amount_1, ..., address_n, amount_n]
555 // with address_i being the encoded bytes of each btc address
556 // and amount_i the RLP-encoded biginteger corresponding to each amount
557 // Order of entries in serialized output is order of the request queue entries
558 // so that we enforce a FIFO policy on release requests.
559 public static byte[] serializeReleaseRequestQueue(ReleaseRequestQueue queue) {
560 List<ReleaseRequestQueue.Entry> entries = queue.getEntriesWithoutHash();
561
562 byte[][] bytes = new byte[entries.size() * 2][];
563 int n = 0;
564
565 for (ReleaseRequestQueue.Entry entry : entries) {
566 bytes[n++] = RLP.encodeElement(entry.getDestination().getHash160());
567 bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getAmount().getValue()));
568 }
569
570 return RLP.encodeList(bytes);
571 }
572
573 public static byte[] serializeReleaseRequestQueueWithTxHash(ReleaseRequestQueue queue) {
574 List<ReleaseRequestQueue.Entry> entries = queue.getEntriesWithHash();
575
576 byte[][] bytes = new byte[entries.size() * 3][];
577 int n = 0;
578
579 for (ReleaseRequestQueue.Entry entry : entries) {
580 bytes[n++] = RLP.encodeElement(entry.getDestination().getHash160());
581 bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getAmount().getValue()));
582 bytes[n++] = RLP.encodeElement(entry.getRskTxHash().getBytes());
583 }
584
585 return RLP.encodeList(bytes);
586 }
587
588 public static List<ReleaseRequestQueue.Entry> deserializeReleaseRequestQueue(byte[] data, NetworkParameters networkParameters) {
589 return deserializeReleaseRequestQueue(data, networkParameters, false);
590 }
591
592 public static List<ReleaseRequestQueue.Entry> deserializeReleaseRequestQueue(byte[] data, NetworkParameters networkParameters, boolean hasTxHash) {
593 if (data == null || data.length == 0) {
594 return new ArrayList<>();
595 }
596
597 int elementsMultipleCount = hasTxHash ? 3 : 2;
598 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
599
600 // Must have an even number of items
601 if (rlpList.size() % elementsMultipleCount != 0) {
602 throw new RuntimeException(String.format("Invalid serialized ReleaseRequestQueue. Expected a multiple of %d number of elements, but got %d", elementsMultipleCount, rlpList.size()));
603 }
604
605 return hasTxHash ? deserializeReleaseRequestQueueWithTxHash(rlpList, networkParameters) : deserializeReleaseRequestQueueWithoutTxHash(rlpList, networkParameters);
606 }
607
608 // For the serialization format, see BridgeSerializationUtils::serializeReleaseRequestQueue
609 private static List<ReleaseRequestQueue.Entry> deserializeReleaseRequestQueueWithoutTxHash(RLPList rlpList, NetworkParameters networkParameters) {
610 List<ReleaseRequestQueue.Entry> entries = new ArrayList<>();
611
612 int n = rlpList.size() / 2;
613 for (int k = 0; k < n; k++) {
614 byte[] addressBytes = rlpList.get(k * 2).getRLPData();
615 Address address = new Address(networkParameters, addressBytes);
616 long amount = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 2 + 1).getRLPData()).longValue();
617
618 entries.add(new ReleaseRequestQueue.Entry(address, Coin.valueOf(amount), null));
619 }
620
621 return entries;
622 }
623
624 // For the serialization format, see BridgeSerializationUtils::serializeReleaseRequestQueue
625 private static List<ReleaseRequestQueue.Entry> deserializeReleaseRequestQueueWithTxHash(RLPList rlpList, NetworkParameters networkParameters) {
626 List<ReleaseRequestQueue.Entry> entries = new ArrayList<>();
627
628 int n = rlpList.size() / 3;
629 for (int k = 0; k < n; k++) {
630 byte[] addressBytes = rlpList.get(k * 3).getRLPData();
631 Address address = new Address(networkParameters, addressBytes);
632 long amount = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 3 + 1).getRLPData()).longValue();
633 Keccak256 txHash = new Keccak256(rlpList.get(k * 3 + 2).getRLPData());
634
635 entries.add(new ReleaseRequestQueue.Entry(address, Coin.valueOf(amount), txHash));
636 }
637
638 return entries;
639 }
640
641 // A ReleaseTransactionSet is serialized as follows:
642 // [btctx_1, height_1, ..., btctx_n, height_n]
643 // with btctx_i being the bitcoin serialization of each btc tx
644 // and height_i the RLP-encoded biginteger corresponding to each height
645 // To preserve order amongst different implementations of sets,
646 // entries are first sorted on the lexicographical order of the
647 // serialized btc transaction bytes
648 // (see ReleaseTransactionSet.Entry.BTC_TX_COMPARATOR)
649 public static byte[] serializeReleaseTransactionSet(ReleaseTransactionSet set) {
650 List<ReleaseTransactionSet.Entry> entries = set.getEntriesWithoutHash().stream().collect(Collectors.toList());
651 entries.sort(ReleaseTransactionSet.Entry.BTC_TX_COMPARATOR);
652
653 byte[][] bytes = new byte[entries.size() * 2][];
654 int n = 0;
655
656 for (ReleaseTransactionSet.Entry entry : entries) {
657 bytes[n++] = RLP.encodeElement(entry.getTransaction().bitcoinSerialize());
658 bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getRskBlockNumber()));
659 }
660
661 return RLP.encodeList(bytes);
662 }
663
664 public static byte[] serializeReleaseTransactionSetWithTxHash(ReleaseTransactionSet set) {
665 List<ReleaseTransactionSet.Entry> entries = new ArrayList<>(set.getEntriesWithHash());
666 entries.sort(ReleaseTransactionSet.Entry.BTC_TX_COMPARATOR);
667
668 byte[][] bytes = new byte[entries.size() * 3][];
669 int n = 0;
670
671 for (ReleaseTransactionSet.Entry entry : entries) {
672 bytes[n++] = RLP.encodeElement(entry.getTransaction().bitcoinSerialize());
673 bytes[n++] = RLP.encodeBigInteger(BigInteger.valueOf(entry.getRskBlockNumber()));
674 bytes[n++] = RLP.encodeElement(entry.getRskTxHash().getBytes());
675 }
676
677 return RLP.encodeList(bytes);
678 }
679
680 public static ReleaseTransactionSet deserializeReleaseTransactionSet(byte[] data, NetworkParameters networkParameters) {
681 return deserializeReleaseTransactionSet(data, networkParameters, false);
682 }
683
684 public static ReleaseTransactionSet deserializeReleaseTransactionSet(byte[] data, NetworkParameters networkParameters, boolean hasTxHash) {
685 if (data == null || data.length == 0) {
686 return new ReleaseTransactionSet(new HashSet<>());
687 }
688
689 int elementsMultipleCount = hasTxHash ? 3 : 2;
690 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
691
692 // Must have an even number of items
693 if (rlpList.size() % elementsMultipleCount != 0) {
694 throw new RuntimeException(String.format("Invalid serialized ReleaseTransactionSet. Expected a multiple of %d number of elements, but got %d", elementsMultipleCount, rlpList.size()));
695 }
696
697 return hasTxHash ? deserializeReleaseTransactionSetWithTxHash(rlpList, networkParameters) : deserializeReleaseTransactionSetWithoutTxHash(rlpList, networkParameters);
698 }
699
700 // For the serialization format, see BridgeSerializationUtils::serializeReleaseTransactionSet
701 private static ReleaseTransactionSet deserializeReleaseTransactionSetWithoutTxHash(RLPList rlpList, NetworkParameters networkParameters) {
702 Set<ReleaseTransactionSet.Entry> entries = new HashSet<>();
703
704 int n = rlpList.size() / 2;
705 for (int k = 0; k < n; k++) {
706 byte[] txPayload = rlpList.get(k * 2).getRLPData();
707 BtcTransaction tx = new BtcTransaction(networkParameters, txPayload);
708
709 long height = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 2 + 1).getRLPData()).longValue();
710
711 entries.add(new ReleaseTransactionSet.Entry(tx, height));
712 }
713
714 return new ReleaseTransactionSet(entries);
715 }
716
717 private static ReleaseTransactionSet deserializeReleaseTransactionSetWithTxHash(RLPList rlpList, NetworkParameters networkParameters) {
718 Set<ReleaseTransactionSet.Entry> entries = new HashSet<>();
719
720 int n = rlpList.size() / 3;
721 for (int k = 0; k < n; k++) {
722 byte[] txPayload = rlpList.get(k * 3).getRLPData();
723 BtcTransaction tx = new BtcTransaction(networkParameters, txPayload);
724
725 long height = BigIntegers.fromUnsignedByteArray(rlpList.get(k * 3 + 1).getRLPData()).longValue();
726 Keccak256 rskTxHash = new Keccak256(rlpList.get(k * 3 + 2).getRLPData());
727
728 entries.add(new ReleaseTransactionSet.Entry(tx, height, rskTxHash));
729 }
730
731 return new ReleaseTransactionSet(entries);
732 }
733
734 public static byte[] serializeInteger(Integer value) {
735 return RLP.encodeBigInteger(BigInteger.valueOf(value));
736 }
737
738 public static Integer deserializeInteger(byte[] data) {
739 return RLP.decodeBigInteger(data, 0).intValue();
740 }
741
742 public static byte[] serializeLong(long value) { return RLP.encodeBigInteger(BigInteger.valueOf(value)); }
743
744 public static Optional<Long> deserializeOptionalLong(byte[] data) {
745 if (data == null) {
746 return Optional.empty();
747 }
748 return Optional.of(RLP.decodeBigInteger(data, 0).longValue());
749 }
750
751 public static CoinbaseInformation deserializeCoinbaseInformation(byte[] data) {
752 if (data == null) {
753 return null;
754 }
755 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
756
757 if (rlpList.size() != 1) {
758 throw new RuntimeException(String.format("Invalid serialized coinbase information, expected 1 value but got %d", rlpList.size()));
759 }
760
761 Sha256Hash witnessMerkleRoot = Sha256Hash.wrap(rlpList.get(0).getRLPData());
762
763 return new CoinbaseInformation(witnessMerkleRoot);
764 }
765
766 public static byte[] serializeCoinbaseInformation(CoinbaseInformation coinbaseInformation) {
767 if (coinbaseInformation == null) {
768 return null;
769 }
770 byte[][] rlpElements = new byte[1][];
771 rlpElements[0] = RLP.encodeElement(coinbaseInformation.getWitnessMerkleRoot().getBytes());
772 return RLP.encodeList(rlpElements);
773 }
774
775 public static byte[] serializeSha256Hash(Sha256Hash hash) {
776 return RLP.encodeElement(hash.getBytes());
777 }
778
779 public static Sha256Hash deserializeSha256Hash(byte[] data) {
780 RLPElement element = RLP.decodeFirstElement(data, 0);
781 if (element == null) {
782 return null;
783 }
784 return Sha256Hash.wrap(element.getRLPData());
785 }
786
787 public static byte[] serializeScript(Script script) {
788 return RLP.encodeList(RLP.encodeElement(script.getProgram()));
789 }
790
791 @Nullable
792 public static Script deserializeScript(byte[] data) {
793 if (data == null) {
794 return null;
795 }
796
797 RLPList rlpList = (RLPList) RLP.decode2(data).get(0);
798 if (rlpList.size() != 1) {
799 throw new RuntimeException(String.format("Invalid serialized script. Expected 1 element, but got %d", rlpList.size()));
800 }
801
802 return new Script(rlpList.get(0).getRLPRawData());
803 }
804
805 public static FastBridgeFederationInformation deserializeFastBridgeInformation(byte[] data, byte[] fastBridgeScriptHash) {
806 if ((data == null) || (data.length == 0)) {
807 return null;
808 }
809
810 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
811
812 if (rlpList.size() != 2) {
813 throw new RuntimeException(String.format("Invalid serialized Fast Bridge Federation: expected 2 value but got %d", rlpList.size()));
814 }
815 Keccak256 derivationHash = new Keccak256(rlpList.get(0).getRLPData());
816 byte[] federationP2SH = rlpList.get(1).getRLPData();
817
818 return new FastBridgeFederationInformation(derivationHash, federationP2SH, fastBridgeScriptHash);
819 }
820
821 public static byte[] serializeFastBridgeInformation(FastBridgeFederationInformation fastBridgeFederationP2SH) {
822 if (fastBridgeFederationP2SH == null) {
823 return new byte[]{};
824 }
825 byte[][] rlpElements = new byte[2][];
826 rlpElements[0] = RLP.encodeElement(fastBridgeFederationP2SH.getDerivationHash().getBytes());
827 rlpElements[1] = RLP.encodeElement(fastBridgeFederationP2SH.getFederationScriptHash());
828
829 return RLP.encodeList(rlpElements);
830 }
831
832 // An ABI call spec is serialized as:
833 // function name encoded in UTF-8
834 // arg_1, ..., arg_n
835 private static byte[] serializeABICallSpec(ABICallSpec spec) {
836 byte[][] encodedArguments = Arrays.stream(spec.getArguments())
837 .map(arg -> RLP.encodeElement(arg))
838 .toArray(byte[][]::new);
839 return RLP.encodeList(
840 RLP.encodeElement(spec.getFunction().getBytes(StandardCharsets.UTF_8)),
841 RLP.encodeList(encodedArguments)
842 );
843 }
844
845 // For the serialization format, see BridgeSerializationUtils::serializeABICallSpec
846 private static ABICallSpec deserializeABICallSpec(byte[] data) {
847 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
848
849 if (rlpList.size() != 2) {
850 throw new RuntimeException(String.format("Invalid serialized ABICallSpec. Expected 2 elements, but got %d", rlpList.size()));
851 }
852
853 String function = new String(rlpList.get(0).getRLPData(), StandardCharsets.UTF_8);
854 RLPList rlpArguments = (RLPList)rlpList.get(1);
855 byte[][] arguments = new byte[rlpArguments.size()][];
856
857 for (int k = 0; k < rlpArguments.size(); k++) {
858 arguments[k] = rlpArguments.get(k).getRLPData();
859 }
860
861 return new ABICallSpec(function, arguments);
862 }
863
864 // A list of btc public keys is serialized as
865 // [pubkey1, pubkey2, ..., pubkeyn], sorted
866 // using the lexicographical order of the public keys
867 // (see BtcECKey.PUBKEY_COMPARATOR).
868 private static byte[] serializeBtcPublicKeys(List<BtcECKey> keys) {
869 List<byte[]> encodedKeys = keys.stream()
870 .sorted(BtcECKey.PUBKEY_COMPARATOR)
871 .map(key -> RLP.encodeElement(key.getPubKey()))
872 .collect(Collectors.toList());
873 return RLP.encodeList(encodedKeys.toArray(new byte[0][]));
874 }
875
876 // For the serialization format, see BridgeSerializationUtils::serializePublicKeys
877 private static List<BtcECKey> deserializeBtcPublicKeys(byte[] data) {
878 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
879
880 List<BtcECKey> keys = new ArrayList<>();
881
882 for (int k = 0; k < rlpList.size(); k++) {
883 RLPElement element = rlpList.get(k);
884 BtcECKey key = BtcECKey.fromPublicOnly(element.getRLPData());
885 keys.add(key);
886 }
887
888 return keys;
889 }
890
891 // A list of voters is serialized as
892 // [voterBytes1, voterBytes2, ..., voterBytesn], sorted
893 // using the lexicographical order of the voters' unsigned bytes
894 private static byte[] serializeVoters(List<RskAddress> voters) {
895 List<byte[]> encodedKeys = voters.stream()
896 .sorted(RskAddress.LEXICOGRAPHICAL_COMPARATOR)
897 .map(key -> RLP.encodeElement(key.getBytes()))
898 .collect(Collectors.toList());
899 return RLP.encodeList(encodedKeys.toArray(new byte[0][]));
900 }
901
902 // For the serialization format, see BridgeSerializationUtils::serializeVoters
903 private static List<RskAddress> deserializeVoters(byte[] data) {
904 RLPList rlpList = (RLPList)RLP.decode2(data).get(0);
905
906 List<RskAddress> addresses = new ArrayList<>();
907
908 for (int k = 0; k < rlpList.size(); k++) {
909 RLPElement element = rlpList.get(k);
910 RskAddress address = new RskAddress(element.getRLPData());
911 addresses.add(address);
912 }
913
914 return addresses;
915 }
916 }