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 }