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 }