Coverage Summary for Class: BridgeStorageProvider (co.rsk.peg)

Class Class, % Method, % Line, %
BridgeStorageProvider 0% (0/1) 0% (0/93) 0% (0/375)


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.LockWhitelist; 29 import co.rsk.peg.whitelist.LockWhitelistEntry; 30 import co.rsk.peg.whitelist.OneOffWhiteListEntry; 31 import co.rsk.peg.whitelist.UnlimitedWhiteListEntry; 32 import org.apache.commons.lang3.tuple.Pair; 33 import org.ethereum.config.blockchain.upgrades.ActivationConfig; 34 import org.ethereum.core.Repository; 35 import org.ethereum.vm.DataWord; 36 import org.spongycastle.util.encoders.Hex; 37  38 import java.io.IOException; 39 import java.util.*; 40  41 import static org.ethereum.config.blockchain.upgrades.ConsensusRule.*; 42  43 /** 44  * Provides an object oriented facade of the bridge contract memory. 45  * @see co.rsk.remasc.RemascStorageProvider 46  * @author ajlopez 47  * @author Oscar Guindzberg 48  */ 49 public class BridgeStorageProvider { 50  private static final DataWord NEW_FEDERATION_BTC_UTXOS_KEY = DataWord.fromString("newFederationBtcUTXOs"); 51  private static final DataWord OLD_FEDERATION_BTC_UTXOS_KEY = DataWord.fromString("oldFederationBtcUTXOs"); 52  private static final DataWord BTC_TX_HASHES_ALREADY_PROCESSED_KEY = DataWord.fromString("btcTxHashesAP"); 53  private static final DataWord RELEASE_REQUEST_QUEUE = DataWord.fromString("releaseRequestQueue"); 54  private static final DataWord RELEASE_TX_SET = DataWord.fromString("releaseTransactionSet"); 55  private static final DataWord RSK_TXS_WAITING_FOR_SIGNATURES_KEY = DataWord.fromString("rskTxsWaitingFS"); 56  private static final DataWord NEW_FEDERATION_KEY = DataWord.fromString("newFederation"); 57  private static final DataWord OLD_FEDERATION_KEY = DataWord.fromString("oldFederation"); 58  private static final DataWord PENDING_FEDERATION_KEY = DataWord.fromString("pendingFederation"); 59  private static final DataWord FEDERATION_ELECTION_KEY = DataWord.fromString("federationElection"); 60  private static final DataWord LOCK_ONE_OFF_WHITELIST_KEY = DataWord.fromString("lockWhitelist"); 61  private static final DataWord LOCK_UNLIMITED_WHITELIST_KEY = DataWord.fromString("unlimitedLockWhitelist"); 62  private static final DataWord FEE_PER_KB_KEY = DataWord.fromString("feePerKb"); 63  private static final DataWord FEE_PER_KB_ELECTION_KEY = DataWord.fromString("feePerKbElection"); 64  private static final DataWord LOCKING_CAP_KEY = DataWord.fromString("lockingCap"); 65  private static final DataWord RELEASE_REQUEST_QUEUE_WITH_TXHASH = DataWord.fromString("releaseRequestQueueWithTxHash"); 66  private static final DataWord RELEASE_TX_SET_WITH_TXHASH = DataWord.fromString("releaseTransactionSetWithTxHash"); 67  private static final DataWord RECEIVE_HEADERS_TIMESTAMP = DataWord.fromString("receiveHeadersLastTimestamp"); 68  69  // Federation creation keys 70  private static final DataWord ACTIVE_FEDERATION_CREATION_BLOCK_HEIGHT_KEY = DataWord.fromString("activeFedCreationBlockHeight"); 71  private static final DataWord NEXT_FEDERATION_CREATION_BLOCK_HEIGHT_KEY = DataWord.fromString("nextFedCreationBlockHeight"); 72  private static final DataWord LAST_RETIRED_FEDERATION_P2SH_SCRIPT_KEY = DataWord.fromString("lastRetiredFedP2SHScript"); 73  74  // Version keys and versions 75  private static final DataWord NEW_FEDERATION_FORMAT_VERSION = DataWord.fromString("newFederationFormatVersion"); 76  private static final DataWord OLD_FEDERATION_FORMAT_VERSION = DataWord.fromString("oldFederationFormatVersion"); 77  private static final DataWord PENDING_FEDERATION_FORMAT_VERSION = DataWord.fromString("pendingFederationFormatVersion"); 78  private static final Integer FEDERATION_FORMAT_VERSION_MULTIKEY = 1000; 79  private static final Integer ERP_FEDERATION_FORMAT_VERSION = 2000; 80  81  // Dummy value to use when saved Fast Bridge Derivation Argument Hash 82  private static final byte FAST_BRIDGE_FEDERATION_DERIVATION_ARGUMENTS_HASH_TRUE_VALUE = (byte) 1; 83  84  private final Repository repository; 85  private final RskAddress contractAddress; 86  private final NetworkParameters networkParameters; 87  private final ActivationConfig.ForBlock activations; 88  private final BridgeConstants bridgeConstants; 89  90  private Map<Sha256Hash, Long> btcTxHashesAlreadyProcessed; 91  92  // RSK release txs follow these steps: First, they are waiting for coin selection (releaseRequestQueue), 93  // then they are waiting for enough confirmations on the RSK network (releaseTransactionSet), 94  // then they are waiting for federators' signatures (rskTxsWaitingForSignatures), 95  // then they are logged into the block that has them as completely signed for btc release 96  // and are removed from rskTxsWaitingForSignatures. 97  // key = rsk tx hash, value = btc tx 98  private ReleaseRequestQueue releaseRequestQueue; 99  private ReleaseTransactionSet releaseTransactionSet; 100  private SortedMap<Keccak256, BtcTransaction> rskTxsWaitingForSignatures; 101  102  private List<UTXO> newFederationBtcUTXOs; 103  private List<UTXO> oldFederationBtcUTXOs; 104  105  private Federation newFederation; 106  private Federation oldFederation; 107  private boolean shouldSaveOldFederation = false; 108  private PendingFederation pendingFederation; 109  private boolean shouldSavePendingFederation = false; 110  111  private ABICallElection federationElection; 112  113  private LockWhitelist lockWhitelist; 114  115  private Coin feePerKb; 116  private ABICallElection feePerKbElection; 117  118  private Coin lockingCap; 119  120  private HashMap<DataWord, Optional<Integer>> storageVersion; 121  122  private HashMap<Sha256Hash, Long> btcTxHashesToSave; 123  124  private Map<Sha256Hash, CoinbaseInformation> coinbaseInformationMap; 125  private Map<Integer, Sha256Hash> btcBlocksIndex; 126  127  private Long activeFederationCreationBlockHeight; 128  private Long nextFederationCreationBlockHeight; // if -1, then clear value 129  private Script lastRetiredFederationP2SHScript; 130  131  private Keccak256 fastBridgeDerivationArgumentsHashToSave = null; 132  private Sha256Hash fastBridgeBtcTxHashToSave = null; 133  private FastBridgeFederationInformation fastBridgeFederationInformationsToSave = null; 134  private long receiveHeadersLastTimestamp = 0; 135  136  public BridgeStorageProvider( 137  Repository repository, 138  RskAddress contractAddress, 139  BridgeConstants bridgeConstants, 140  ActivationConfig.ForBlock activations) { 141  142  this.repository = repository; 143  this.contractAddress = contractAddress; 144  this.networkParameters = bridgeConstants.getBtcParams(); 145  this.activations = activations; 146  this.storageVersion = new HashMap<>(); 147  this.bridgeConstants = bridgeConstants; 148  } 149  150  public List<UTXO> getNewFederationBtcUTXOs() throws IOException { 151  if (newFederationBtcUTXOs != null) { 152  return newFederationBtcUTXOs; 153  } 154  155  newFederationBtcUTXOs = getFromRepository(NEW_FEDERATION_BTC_UTXOS_KEY, BridgeSerializationUtils::deserializeUTXOList); 156  return newFederationBtcUTXOs; 157  } 158  159  public void saveNewFederationBtcUTXOs() throws IOException { 160  if (newFederationBtcUTXOs == null) { 161  return; 162  } 163  164  saveToRepository(NEW_FEDERATION_BTC_UTXOS_KEY, newFederationBtcUTXOs, BridgeSerializationUtils::serializeUTXOList); 165  } 166  167  public List<UTXO> getOldFederationBtcUTXOs() throws IOException { 168  if (oldFederationBtcUTXOs != null) { 169  return oldFederationBtcUTXOs; 170  } 171  172  oldFederationBtcUTXOs = getFromRepository(OLD_FEDERATION_BTC_UTXOS_KEY, BridgeSerializationUtils::deserializeUTXOList); 173  return oldFederationBtcUTXOs; 174  } 175  176  public void saveOldFederationBtcUTXOs() throws IOException { 177  if (oldFederationBtcUTXOs == null) { 178  return; 179  } 180  181  saveToRepository(OLD_FEDERATION_BTC_UTXOS_KEY, oldFederationBtcUTXOs, BridgeSerializationUtils::serializeUTXOList); 182  } 183  184  public Optional<Long> getHeightIfBtcTxhashIsAlreadyProcessed(Sha256Hash btcTxHash) throws IOException { 185  Map<Sha256Hash, Long> processed = getBtcTxHashesAlreadyProcessed(); 186  if (processed.containsKey(btcTxHash)) { 187  return Optional.of(processed.get(btcTxHash)); 188  } 189  190  if (!activations.isActive(RSKIP134)) { 191  return Optional.empty(); 192  } 193  194  if (btcTxHashesToSave == null) { 195  btcTxHashesToSave = new HashMap<>(); 196  } 197  198  if (btcTxHashesToSave.containsKey(btcTxHash)) { 199  return Optional.of(btcTxHashesToSave.get(btcTxHash)); 200  } 201  202  Optional<Long> height = getFromRepository(getStorageKeyForBtcTxHashAlreadyProcessed(btcTxHash), BridgeSerializationUtils::deserializeOptionalLong); 203  if (!height.isPresent()) { 204  return height; 205  } 206  207  btcTxHashesToSave.put(btcTxHash, height.get()); 208  return height; 209  } 210  211  public void setHeightBtcTxhashAlreadyProcessed(Sha256Hash btcTxHash, long height) throws IOException { 212  if (activations.isActive(RSKIP134)) { 213  if (btcTxHashesToSave == null) { 214  btcTxHashesToSave = new HashMap<>(); 215  } 216  btcTxHashesToSave.put(btcTxHash, height); 217  } else { 218  getBtcTxHashesAlreadyProcessed().put(btcTxHash, height); 219  } 220  } 221  222  public void saveHeightBtcTxHashAlreadyProcessed() { 223  if (btcTxHashesToSave == null) { 224  return; 225  } 226  227  btcTxHashesToSave.forEach((btcTxHash, height) -> 228  safeSaveToRepository(getStorageKeyForBtcTxHashAlreadyProcessed(btcTxHash), height, BridgeSerializationUtils::serializeLong) 229  ); 230  } 231  232  private Map<Sha256Hash, Long> getBtcTxHashesAlreadyProcessed() throws IOException { 233  if (btcTxHashesAlreadyProcessed != null) { 234  return btcTxHashesAlreadyProcessed; 235  } 236  237  btcTxHashesAlreadyProcessed = getFromRepository(BTC_TX_HASHES_ALREADY_PROCESSED_KEY, BridgeSerializationUtils::deserializeMapOfHashesToLong); 238  return btcTxHashesAlreadyProcessed; 239  } 240  241  public void saveBtcTxHashesAlreadyProcessed() { 242  if (btcTxHashesAlreadyProcessed == null) { 243  return; 244  } 245  246  safeSaveToRepository(BTC_TX_HASHES_ALREADY_PROCESSED_KEY, btcTxHashesAlreadyProcessed, BridgeSerializationUtils::serializeMapOfHashesToLong); 247  } 248  249  public ReleaseRequestQueue getReleaseRequestQueue() throws IOException { 250  if (releaseRequestQueue != null) { 251  return releaseRequestQueue; 252  } 253  254  List<ReleaseRequestQueue.Entry> entries = new ArrayList<>(); 255  256  entries.addAll(getFromRepository( 257  RELEASE_REQUEST_QUEUE, 258  data -> BridgeSerializationUtils.deserializeReleaseRequestQueue(data, networkParameters) 259  ) 260  ); 261  262  if (!activations.isActive(RSKIP146)) { 263  releaseRequestQueue = new ReleaseRequestQueue(entries); 264  return releaseRequestQueue; 265  } 266  267  entries.addAll(getFromRepository( 268  RELEASE_REQUEST_QUEUE_WITH_TXHASH, 269  data -> BridgeSerializationUtils.deserializeReleaseRequestQueue(data, networkParameters, true) 270  ) 271  ); 272  273  releaseRequestQueue = new ReleaseRequestQueue(entries); 274  275  return releaseRequestQueue; 276  } 277  278  public void saveReleaseRequestQueue() { 279  if (releaseRequestQueue == null) { 280  return; 281  } 282  283  safeSaveToRepository(RELEASE_REQUEST_QUEUE, releaseRequestQueue, BridgeSerializationUtils::serializeReleaseRequestQueue); 284  285  if(activations.isActive(RSKIP146)) { 286  safeSaveToRepository(RELEASE_REQUEST_QUEUE_WITH_TXHASH, releaseRequestQueue, BridgeSerializationUtils::serializeReleaseRequestQueueWithTxHash); 287  } 288  } 289  290  public ReleaseTransactionSet getReleaseTransactionSet() throws IOException { 291  if (releaseTransactionSet != null) { 292  return releaseTransactionSet; 293  } 294  295  Set<ReleaseTransactionSet.Entry> entries = new HashSet<>(getFromRepository(RELEASE_TX_SET, 296  data -> BridgeSerializationUtils.deserializeReleaseTransactionSet(data, networkParameters).getEntries())); 297  298  if (!activations.isActive(RSKIP146)) { 299  releaseTransactionSet = new ReleaseTransactionSet(entries); 300  return releaseTransactionSet; 301  } 302  303  entries.addAll(getFromRepository( 304  RELEASE_TX_SET_WITH_TXHASH, 305  data -> BridgeSerializationUtils.deserializeReleaseTransactionSet(data, networkParameters, true).getEntries())); 306  307  releaseTransactionSet = new ReleaseTransactionSet(entries); 308  309  return releaseTransactionSet; 310  } 311  312  public void saveReleaseTransactionSet() { 313  if (releaseTransactionSet == null) { 314  return; 315  } 316  317  safeSaveToRepository(RELEASE_TX_SET, releaseTransactionSet, BridgeSerializationUtils::serializeReleaseTransactionSet); 318  319  if (activations.isActive(RSKIP146)) { 320  safeSaveToRepository(RELEASE_TX_SET_WITH_TXHASH, releaseTransactionSet, BridgeSerializationUtils::serializeReleaseTransactionSetWithTxHash); 321  } 322  } 323  324  public SortedMap<Keccak256, BtcTransaction> getRskTxsWaitingForSignatures() throws IOException { 325  if (rskTxsWaitingForSignatures != null) { 326  return rskTxsWaitingForSignatures; 327  } 328  329  rskTxsWaitingForSignatures = getFromRepository( 330  RSK_TXS_WAITING_FOR_SIGNATURES_KEY, 331  data -> BridgeSerializationUtils.deserializeMap(data, networkParameters, false) 332  ); 333  return rskTxsWaitingForSignatures; 334  } 335  336  public void saveRskTxsWaitingForSignatures() { 337  if (rskTxsWaitingForSignatures == null) { 338  return; 339  } 340  341  safeSaveToRepository(RSK_TXS_WAITING_FOR_SIGNATURES_KEY, rskTxsWaitingForSignatures, BridgeSerializationUtils::serializeMap); 342  } 343  344  public Federation getNewFederation() { 345  if (newFederation != null) { 346  return newFederation; 347  } 348  349  Optional<Integer> storageVersion = getStorageVersion(NEW_FEDERATION_FORMAT_VERSION); 350  351  newFederation = safeGetFromRepository( 352  NEW_FEDERATION_KEY, 353  data -> { 354  if (data == null) { 355  return null; 356  } 357  if (storageVersion.isPresent()) { 358  return deserializeFederationAccordingToVersion(data, storageVersion.get(), bridgeConstants); 359  } 360  361  return BridgeSerializationUtils.deserializeFederationOnlyBtcKeys(data, networkParameters); 362  } 363  ); 364  365  return newFederation; 366  } 367  368  public void setNewFederation(Federation federation) { 369  newFederation = federation; 370  } 371  372  /** 373  * Save the new federation 374  * Only saved if a federation was set with BridgeStorageProvider::setNewFederation 375  */ 376  public void saveNewFederation() { 377  if (newFederation == null) { 378  return; 379  } 380  381  RepositorySerializer<Federation> serializer = BridgeSerializationUtils::serializeFederationOnlyBtcKeys; 382  383  if (activations.isActive(RSKIP123)) { 384  if (activations.isActive(RSKIP201) && newFederation instanceof ErpFederation) { 385  saveStorageVersion( 386  NEW_FEDERATION_FORMAT_VERSION, 387  ERP_FEDERATION_FORMAT_VERSION 388  ); 389  } else { 390  saveStorageVersion( 391  NEW_FEDERATION_FORMAT_VERSION, 392  FEDERATION_FORMAT_VERSION_MULTIKEY 393  ); 394  } 395  serializer = BridgeSerializationUtils::serializeFederation; 396  } 397  398  safeSaveToRepository(NEW_FEDERATION_KEY, newFederation, serializer); 399  } 400  401  public Federation getOldFederation() { 402  if (oldFederation != null || shouldSaveOldFederation) { 403  return oldFederation; 404  } 405  406  Optional<Integer> storageVersion = getStorageVersion(OLD_FEDERATION_FORMAT_VERSION); 407  408  oldFederation = safeGetFromRepository( 409  OLD_FEDERATION_KEY, 410  data -> { 411  if (data == null) { 412  return null; 413  } 414  if (storageVersion.isPresent()) { 415  return deserializeFederationAccordingToVersion(data, storageVersion.get(), bridgeConstants); 416  } 417  418  return BridgeSerializationUtils.deserializeFederationOnlyBtcKeys(data, networkParameters); 419  } 420  ); 421  422  return oldFederation; 423  } 424  425  public void setOldFederation(Federation federation) { 426  shouldSaveOldFederation = true; 427  oldFederation = federation; 428  } 429  430  /** 431  * Save the old federation 432  */ 433  public void saveOldFederation() { 434  if (shouldSaveOldFederation) { 435  RepositorySerializer<Federation> serializer = 436  BridgeSerializationUtils::serializeFederationOnlyBtcKeys; 437  438  if (activations.isActive(RSKIP123)) { 439  if (activations.isActive(RSKIP201) && oldFederation instanceof ErpFederation) { 440  saveStorageVersion( 441  OLD_FEDERATION_FORMAT_VERSION, 442  ERP_FEDERATION_FORMAT_VERSION 443  ); 444  } else { 445  saveStorageVersion( 446  OLD_FEDERATION_FORMAT_VERSION, 447  FEDERATION_FORMAT_VERSION_MULTIKEY 448  ); 449  } 450  451  serializer = BridgeSerializationUtils::serializeFederation; 452  } 453  454  safeSaveToRepository(OLD_FEDERATION_KEY, oldFederation, serializer); 455  } 456  } 457  458  public PendingFederation getPendingFederation() { 459  if (pendingFederation != null || shouldSavePendingFederation) { 460  return pendingFederation; 461  } 462  463  Optional<Integer> storageVersion = getStorageVersion(PENDING_FEDERATION_FORMAT_VERSION); 464  465  pendingFederation = safeGetFromRepository( 466  PENDING_FEDERATION_KEY, 467  data -> { 468  if (data == null) { 469  return null; 470  } 471  if (storageVersion.isPresent()) { 472  return BridgeSerializationUtils.deserializePendingFederation(data); // Assume this is the multi-key version 473  } 474  475  return BridgeSerializationUtils.deserializePendingFederationOnlyBtcKeys(data); 476  } 477  ); 478  479  return pendingFederation; 480  } 481  482  public void setPendingFederation(PendingFederation federation) { 483  shouldSavePendingFederation = true; 484  pendingFederation = federation; 485  } 486  487  /** 488  * Save the pending federation 489  */ 490  public void savePendingFederation() { 491  if (shouldSavePendingFederation) { 492  RepositorySerializer<PendingFederation> serializer = BridgeSerializationUtils::serializePendingFederationOnlyBtcKeys; 493  494  if (activations.isActive(RSKIP123)) { 495  saveStorageVersion(PENDING_FEDERATION_FORMAT_VERSION, FEDERATION_FORMAT_VERSION_MULTIKEY); 496  serializer = BridgeSerializationUtils::serializePendingFederation; 497  } 498  499  safeSaveToRepository(PENDING_FEDERATION_KEY, pendingFederation, serializer); 500  } 501  } 502  503  /** 504  * Save the federation election 505  */ 506  public void saveFederationElection() { 507  if (federationElection == null) { 508  return; 509  } 510  511  safeSaveToRepository(FEDERATION_ELECTION_KEY, federationElection, BridgeSerializationUtils::serializeElection); 512  } 513  514  public ABICallElection getFederationElection(AddressBasedAuthorizer authorizer) { 515  if (federationElection != null) { 516  return federationElection; 517  } 518  519  federationElection = safeGetFromRepository(FEDERATION_ELECTION_KEY, data -> (data == null)? new ABICallElection(authorizer) : BridgeSerializationUtils.deserializeElection(data, authorizer)); 520  return federationElection; 521  } 522  523  /** 524  * Save the lock whitelist 525  */ 526  public void saveLockWhitelist() { 527  if (lockWhitelist == null) { 528  return; 529  } 530  531  List<OneOffWhiteListEntry> oneOffEntries = lockWhitelist.getAll(OneOffWhiteListEntry.class); 532  safeSaveToRepository(LOCK_ONE_OFF_WHITELIST_KEY, Pair.of(oneOffEntries, lockWhitelist.getDisableBlockHeight()), BridgeSerializationUtils::serializeOneOffLockWhitelist); 533  534  if (activations.isActive(RSKIP87)) { 535  List<UnlimitedWhiteListEntry> unlimitedEntries = lockWhitelist.getAll(UnlimitedWhiteListEntry.class); 536  safeSaveToRepository(LOCK_UNLIMITED_WHITELIST_KEY, unlimitedEntries, BridgeSerializationUtils::serializeUnlimitedLockWhitelist); 537  } 538  } 539  540  public LockWhitelist getLockWhitelist() { 541  if (lockWhitelist != null) { 542  return lockWhitelist; 543  } 544  545  Pair<HashMap<Address, OneOffWhiteListEntry>, Integer> oneOffWhitelistAndDisableBlockHeightData = 546  safeGetFromRepository(LOCK_ONE_OFF_WHITELIST_KEY, 547  data -> BridgeSerializationUtils.deserializeOneOffLockWhitelistAndDisableBlockHeight(data, networkParameters)); 548  if (oneOffWhitelistAndDisableBlockHeightData == null) { 549  lockWhitelist = new LockWhitelist(new HashMap<>()); 550  return lockWhitelist; 551  } 552  553  Map<Address, LockWhitelistEntry> whitelistedAddresses = new HashMap<>(); 554  555  whitelistedAddresses.putAll(oneOffWhitelistAndDisableBlockHeightData.getLeft()); 556  557  if (activations.isActive(RSKIP87)) { 558  whitelistedAddresses.putAll(safeGetFromRepository(LOCK_UNLIMITED_WHITELIST_KEY, 559  data -> BridgeSerializationUtils.deserializeUnlimitedLockWhitelistEntries(data, networkParameters))); 560  } 561  562  lockWhitelist = new LockWhitelist(whitelistedAddresses, oneOffWhitelistAndDisableBlockHeightData.getRight()); 563  564  return lockWhitelist; 565  } 566  567  public Coin getFeePerKb() { 568  if (feePerKb != null) { 569  return feePerKb; 570  } 571  572  feePerKb = safeGetFromRepository(FEE_PER_KB_KEY, BridgeSerializationUtils::deserializeCoin); 573  return feePerKb; 574  } 575  576  public void setFeePerKb(Coin feePerKb) { 577  this.feePerKb = feePerKb; 578  } 579  580  public void saveFeePerKb() { 581  if (feePerKb == null) { 582  return; 583  } 584  585  safeSaveToRepository(FEE_PER_KB_KEY, feePerKb, BridgeSerializationUtils::serializeCoin); 586  } 587  588  /** 589  * Save the fee per kb election 590  */ 591  public void saveFeePerKbElection() { 592  if (feePerKbElection == null) { 593  return; 594  } 595  596  safeSaveToRepository(FEE_PER_KB_ELECTION_KEY, feePerKbElection, BridgeSerializationUtils::serializeElection); 597  } 598  599  public ABICallElection getFeePerKbElection(AddressBasedAuthorizer authorizer) { 600  if (feePerKbElection != null) { 601  return feePerKbElection; 602  } 603  604  feePerKbElection = safeGetFromRepository(FEE_PER_KB_ELECTION_KEY, data -> BridgeSerializationUtils.deserializeElection(data, authorizer)); 605  return feePerKbElection; 606  } 607  608  public void saveLockingCap() { 609  if (activations.isActive(RSKIP134)) { 610  safeSaveToRepository(LOCKING_CAP_KEY, this.getLockingCap(), BridgeSerializationUtils::serializeCoin); 611  } 612  } 613  614  public void setLockingCap(Coin lockingCap) { 615  this.lockingCap = lockingCap; 616  } 617  618  public Coin getLockingCap() { 619  if (activations.isActive(RSKIP134)) { 620  if (this.lockingCap == null) { 621  this.lockingCap = safeGetFromRepository(LOCKING_CAP_KEY, BridgeSerializationUtils::deserializeCoin); 622  } 623  return this.lockingCap; 624  } 625  return null; 626  } 627  628  public CoinbaseInformation getCoinbaseInformation(Sha256Hash blockHash) { 629  if (!activations.isActive(RSKIP143)) { 630  return null; 631  } 632  633  if (coinbaseInformationMap == null) { 634  coinbaseInformationMap = new HashMap<>(); 635  } 636  637  if (coinbaseInformationMap.containsKey(blockHash)) { 638  return coinbaseInformationMap.get(blockHash); 639  } 640  641  CoinbaseInformation coinbaseInformation = 642  safeGetFromRepository(getStorageKeyForCoinbaseInformation(blockHash), BridgeSerializationUtils::deserializeCoinbaseInformation); 643  coinbaseInformationMap.put(blockHash, coinbaseInformation); 644  645  return coinbaseInformation; 646  } 647  648  public void setCoinbaseInformation(Sha256Hash blockHash, CoinbaseInformation data) { 649  if (!activations.isActive(RSKIP143)) { 650  return; 651  } 652  653  if (coinbaseInformationMap == null) { 654  coinbaseInformationMap = new HashMap<>(); 655  } 656  657  coinbaseInformationMap.put(blockHash, data); 658  } 659  660  public Optional<Sha256Hash> getBtcBestBlockHashByHeight(int height) { 661  if (!activations.isActive(RSKIP199)) { 662  return Optional.empty(); 663  } 664  665  DataWord storageKey = getStorageKeyForBtcBlockIndex(height); 666  Sha256Hash blockHash = safeGetFromRepository(storageKey, BridgeSerializationUtils::deserializeSha256Hash); 667  if (blockHash != null) { 668  return Optional.of(blockHash); 669  } 670  671  return Optional.empty(); 672  } 673  674  public void setBtcBestBlockHashByHeight(int height, Sha256Hash blockHash) { 675  if (!activations.isActive(RSKIP199)) { 676  return; 677  } 678  679  if (btcBlocksIndex == null) { 680  btcBlocksIndex = new HashMap<>(); 681  } 682  683  btcBlocksIndex.put(height, blockHash); 684  } 685  686  private void saveBtcBlocksIndex() { 687  if (btcBlocksIndex != null) { 688  btcBlocksIndex.forEach((Integer height, Sha256Hash blockHash) -> { 689  DataWord storageKey = getStorageKeyForBtcBlockIndex(height); 690  safeSaveToRepository(storageKey, blockHash, BridgeSerializationUtils::serializeSha256Hash); 691  }); 692  } 693  } 694  695  private void saveCoinbaseInformations() { 696  if (!activations.isActive(RSKIP143)) { 697  return; 698  } 699  700  if (coinbaseInformationMap == null || coinbaseInformationMap.size() == 0) { 701  return; 702  } 703  coinbaseInformationMap.forEach((Sha256Hash blockHash, CoinbaseInformation data) -> 704  safeSaveToRepository(getStorageKeyForCoinbaseInformation(blockHash), data, BridgeSerializationUtils::serializeCoinbaseInformation)); 705  } 706  707  public Optional<Long> getActiveFederationCreationBlockHeight() { 708  if (!activations.isActive(RSKIP186)) { 709  return Optional.empty(); 710  } 711  712  if (activeFederationCreationBlockHeight != null) { 713  return Optional.of(activeFederationCreationBlockHeight); 714  } 715  716  activeFederationCreationBlockHeight = safeGetFromRepository(ACTIVE_FEDERATION_CREATION_BLOCK_HEIGHT_KEY, BridgeSerializationUtils::deserializeOptionalLong).orElse(null); 717  return Optional.ofNullable(activeFederationCreationBlockHeight); 718  } 719  720  public void setActiveFederationCreationBlockHeight(long activeFederationCreationBlockHeight) { 721  this.activeFederationCreationBlockHeight = activeFederationCreationBlockHeight; 722  } 723  724  protected void saveActiveFederationCreationBlockHeight() { 725  if (activeFederationCreationBlockHeight == null || !activations.isActive(RSKIP186)) { 726  return; 727  } 728  729  safeSaveToRepository(ACTIVE_FEDERATION_CREATION_BLOCK_HEIGHT_KEY, activeFederationCreationBlockHeight, BridgeSerializationUtils::serializeLong); 730  } 731  732  public Optional<Long> getNextFederationCreationBlockHeight() { 733  if (!activations.isActive(RSKIP186)) { 734  return Optional.empty(); 735  } 736  737  if (nextFederationCreationBlockHeight != null) { 738  return Optional.of(nextFederationCreationBlockHeight); 739  } 740  741  nextFederationCreationBlockHeight = safeGetFromRepository(NEXT_FEDERATION_CREATION_BLOCK_HEIGHT_KEY, BridgeSerializationUtils::deserializeOptionalLong).orElse(null); 742  return Optional.ofNullable(nextFederationCreationBlockHeight); 743  } 744  745  public void setNextFederationCreationBlockHeight(long nextFederationCreationBlockHeight) { 746  this.nextFederationCreationBlockHeight = nextFederationCreationBlockHeight; 747  } 748  749  public void clearNextFederationCreationBlockHeight() { 750  this.nextFederationCreationBlockHeight = -1L; 751  } 752  753  protected void saveNextFederationCreationBlockHeight() { 754  if (nextFederationCreationBlockHeight == null || !activations.isActive(RSKIP186)) { 755  return; 756  } 757  758  if (nextFederationCreationBlockHeight == -1L) { 759  safeSaveToRepository(NEXT_FEDERATION_CREATION_BLOCK_HEIGHT_KEY, null, BridgeSerializationUtils::serializeLong); 760  } else { 761  safeSaveToRepository(NEXT_FEDERATION_CREATION_BLOCK_HEIGHT_KEY, nextFederationCreationBlockHeight, BridgeSerializationUtils::serializeLong); 762  } 763  } 764  765  public Optional<Script> getLastRetiredFederationP2SHScript() { 766  if (!activations.isActive(RSKIP186)) { 767  return Optional.empty(); 768  } 769  770  if (lastRetiredFederationP2SHScript != null) { 771  return Optional.of(lastRetiredFederationP2SHScript); 772  } 773  774  lastRetiredFederationP2SHScript = safeGetFromRepository(LAST_RETIRED_FEDERATION_P2SH_SCRIPT_KEY, BridgeSerializationUtils::deserializeScript); 775  return Optional.ofNullable(lastRetiredFederationP2SHScript); 776  } 777  778  public void setLastRetiredFederationP2SHScript(Script lastRetiredFederationP2SHScript) { 779  this.lastRetiredFederationP2SHScript = lastRetiredFederationP2SHScript; 780  } 781  782  protected void saveLastRetiredFederationP2SHScript() { 783  if (lastRetiredFederationP2SHScript == null || !activations.isActive(RSKIP186)) { 784  return; 785  } 786  787  safeSaveToRepository(LAST_RETIRED_FEDERATION_P2SH_SCRIPT_KEY, lastRetiredFederationP2SHScript, BridgeSerializationUtils::serializeScript); 788  } 789  790  public boolean isFastBridgeFederationDerivationHashUsed(Sha256Hash btcTxHash, Keccak256 derivationArgsHash) { 791  if (!activations.isActive(RSKIP176)) { 792  return false; 793  } 794  795  if (btcTxHash == null || derivationArgsHash == null) { 796  return false; 797  } 798  799  byte[] data = repository.getStorageBytes( 800  contractAddress, 801  getStorageKeyForDerivationArgumentsHash(btcTxHash, derivationArgsHash) 802  ); 803  804  return ((data != null) && (data.length == 1) && (data[0] == FAST_BRIDGE_FEDERATION_DERIVATION_ARGUMENTS_HASH_TRUE_VALUE)); 805  } 806  807  public void markFastBridgeFederationDerivationHashAsUsed(Sha256Hash btcTxHashToSave, Keccak256 derivationArgsHash) { 808  if (activations.isActive(RSKIP176)) { 809  fastBridgeBtcTxHashToSave = btcTxHashToSave; 810  fastBridgeDerivationArgumentsHashToSave = derivationArgsHash; 811  } 812  } 813  814  private void saveDerivationArgumentsHash() { 815  if (fastBridgeDerivationArgumentsHashToSave == null || fastBridgeBtcTxHashToSave == null) { 816  return; 817  } 818  repository.addStorageBytes( 819  contractAddress, 820  getStorageKeyForDerivationArgumentsHash(fastBridgeBtcTxHashToSave, fastBridgeDerivationArgumentsHashToSave), 821  new byte[]{FAST_BRIDGE_FEDERATION_DERIVATION_ARGUMENTS_HASH_TRUE_VALUE} 822  ); 823  } 824  825  public Optional<FastBridgeFederationInformation> getFastBridgeFederationInformation(byte[] fastBridgeScriptHash) { 826  if (!activations.isActive(RSKIP176)) { 827  return Optional.empty(); 828  } 829  830  if (fastBridgeScriptHash == null || fastBridgeScriptHash.length == 0) { 831  return Optional.empty(); 832  } 833  834  FastBridgeFederationInformation fastBridgeFederationInformation = this.safeGetFromRepository( 835  getStorageKeyForfastBridgeFederationInformation(fastBridgeScriptHash), 836  data -> BridgeSerializationUtils.deserializeFastBridgeInformation(data, fastBridgeScriptHash) 837  ); 838  if (fastBridgeFederationInformation == null) { 839  return Optional.empty(); 840  } 841  842  return Optional.of(fastBridgeFederationInformation); 843  } 844  845  public void setFastBridgeFederationInformation(FastBridgeFederationInformation fastBridgeFederationInformation) { 846  if (activations.isActive(RSKIP176)) { 847  this.fastBridgeFederationInformationsToSave = fastBridgeFederationInformation; 848  } 849  } 850  851  private void saveFastBridgeFederationInformation() { 852  if (fastBridgeFederationInformationsToSave == null) { 853  return; 854  } 855  856  safeSaveToRepository( 857  getStorageKeyForfastBridgeFederationInformation( 858  fastBridgeFederationInformationsToSave.getFastBridgeScriptHash() 859  ), 860  fastBridgeFederationInformationsToSave, 861  BridgeSerializationUtils::serializeFastBridgeInformation 862  ); 863  } 864  865  public Optional<Long> getReceiveHeadersLastTimestamp() { 866  if (activations.isActive(RSKIP200)) { 867  return safeGetFromRepository( 868  RECEIVE_HEADERS_TIMESTAMP, 869  BridgeSerializationUtils::deserializeOptionalLong 870  ); 871  } 872  return Optional.empty(); 873  } 874  875  public void setReceiveHeadersLastTimestamp(Long timeInMillis) { 876  if (activations.isActive(RSKIP200)) { 877  receiveHeadersLastTimestamp = timeInMillis; 878  } 879  } 880  881  public void saveReceiveHeadersLastTimestamp() { 882  if (activations.isActive(RSKIP200) && this.receiveHeadersLastTimestamp > 0) { 883  safeSaveToRepository(RECEIVE_HEADERS_TIMESTAMP, this.receiveHeadersLastTimestamp, BridgeSerializationUtils::serializeLong); 884  } 885  } 886  887  public void save() throws IOException { 888  saveBtcTxHashesAlreadyProcessed(); 889  890  saveReleaseRequestQueue(); 891  saveReleaseTransactionSet(); 892  saveRskTxsWaitingForSignatures(); 893  894  saveNewFederation(); 895  saveNewFederationBtcUTXOs(); 896  897  saveOldFederation(); 898  saveOldFederationBtcUTXOs(); 899  900  savePendingFederation(); 901  902  saveFederationElection(); 903  904  saveLockWhitelist(); 905  906  saveFeePerKb(); 907  saveFeePerKbElection(); 908  909  saveLockingCap(); 910  911  saveHeightBtcTxHashAlreadyProcessed(); 912  913  saveCoinbaseInformations(); 914  915  saveActiveFederationCreationBlockHeight(); 916  saveNextFederationCreationBlockHeight(); 917  saveLastRetiredFederationP2SHScript(); 918  919  saveBtcBlocksIndex(); 920  921  saveDerivationArgumentsHash(); 922  saveFastBridgeFederationInformation(); 923  924  saveReceiveHeadersLastTimestamp(); 925  } 926  927  private DataWord getStorageKeyForBtcTxHashAlreadyProcessed(Sha256Hash btcTxHash) { 928  return DataWord.fromLongString("btcTxHashAP-" + btcTxHash.toString()); 929  } 930  931  private DataWord getStorageKeyForCoinbaseInformation(Sha256Hash btcTxHash) { 932  return DataWord.fromLongString("coinbaseInformation-" + btcTxHash.toString()); 933  } 934  935  private DataWord getStorageKeyForBtcBlockIndex(Integer height) { 936  return DataWord.fromLongString("btcBlockHeight-" + height); 937  } 938  939  private DataWord getStorageKeyForDerivationArgumentsHash(Sha256Hash btcTxHash, Keccak256 derivationHash) { 940  return DataWord.fromLongString("fastBridgeHashUsedInBtcTx-" + btcTxHash.toString() + derivationHash.toString()); 941  } 942  943  private DataWord getStorageKeyForfastBridgeFederationInformation(byte[] fastBridgeScriptHash) { 944  return DataWord.fromLongString("fastBridgeFederationInformation-" + Hex.toHexString(fastBridgeScriptHash)); 945  } 946  947  private Optional<Integer> getStorageVersion(DataWord versionKey) { 948  if (!storageVersion.containsKey(versionKey)) { 949  Optional<Integer> version = safeGetFromRepository(versionKey, data -> { 950  if (data == null || data.length == 0) { 951  return Optional.empty(); 952  } 953  954  return Optional.of(BridgeSerializationUtils.deserializeInteger(data)); 955  }); 956  957  storageVersion.put(versionKey, version); 958  return version; 959  } 960  961  return storageVersion.get(versionKey); 962  } 963  964  private void saveStorageVersion(DataWord versionKey, Integer version) { 965  safeSaveToRepository(versionKey, version, BridgeSerializationUtils::serializeInteger); 966  storageVersion.put(versionKey, Optional.of(version)); 967  } 968  969  private Federation deserializeFederationAccordingToVersion( 970  byte[] data, 971  Integer version, 972  BridgeConstants bridgeConstants 973  ) { 974  if (version.equals(ERP_FEDERATION_FORMAT_VERSION)) { 975  return BridgeSerializationUtils.deserializeErpFederation( 976  data, 977  networkParameters, 978  bridgeConstants 979  ); 980  } 981  982  // Assume this is the multi-key version 983  return BridgeSerializationUtils.deserializeFederation(data, networkParameters); 984  } 985  986  private <T> T safeGetFromRepository(DataWord keyAddress, RepositoryDeserializer<T> deserializer) { 987  try { 988  return getFromRepository(keyAddress, deserializer); 989  } catch (IOException ioe) { 990  throw new RuntimeException("Unable to get from repository: " + keyAddress, ioe); 991  } 992  } 993  994  private <T> T getFromRepository(DataWord keyAddress, RepositoryDeserializer<T> deserializer) throws IOException { 995  byte[] data = repository.getStorageBytes(contractAddress, keyAddress); 996  return deserializer.deserialize(data); 997  } 998  999  private <T> void safeSaveToRepository(DataWord addressKey, T object, RepositorySerializer<T> serializer) { 1000  try { 1001  saveToRepository(addressKey, object, serializer); 1002  } catch (IOException ioe) { 1003  throw new RuntimeException("Unable to save to repository: " + addressKey, ioe); 1004  } 1005  } 1006  1007  private <T> void saveToRepository(DataWord addressKey, T object, RepositorySerializer<T> serializer) throws IOException { 1008  byte[] data = null; 1009  if (object != null) { 1010  data = serializer.serialize(object); 1011  } 1012  repository.addStorageBytes(contractAddress, addressKey, data); 1013  } 1014  1015  private interface RepositoryDeserializer<T> { 1016  T deserialize(byte[] data) throws IOException; 1017  } 1018  1019  private interface RepositorySerializer<T> { 1020  byte[] serialize(T object) throws IOException; 1021  } 1022 }