Coverage Summary for Class: ReleaseTransactionSet (co.rsk.peg)
Class |
Method, %
|
Line, %
|
ReleaseTransactionSet |
0%
(0/10)
|
0%
(0/23)
|
ReleaseTransactionSet$Entry |
0%
(0/8)
|
0%
(0/17)
|
ReleaseTransactionSet$Entry$1 |
0%
(0/2)
|
0%
(0/3)
|
Total |
0%
(0/20)
|
0%
(0/43)
|
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.BtcTransaction;
22 import co.rsk.crypto.Keccak256;
23 import com.google.common.primitives.UnsignedBytes;
24
25 import java.util.*;
26 import java.util.stream.Collectors;
27
28 /**
29 * Representation of a queue of BTC release
30 * transactions waiting for confirmations
31 * on the rsk network.
32 *
33 * @author Ariel Mendelzon
34 */
35 public class ReleaseTransactionSet {
36 public static class Entry {
37 // Compares entries using the lexicographical order of the btc tx's serialized bytes
38 public static final Comparator<Entry> BTC_TX_COMPARATOR = new Comparator<Entry>() {
39 private Comparator<byte[]> comparator = UnsignedBytes.lexicographicalComparator();
40
41 @Override
42 public int compare(Entry e1, Entry e2) {
43 return comparator.compare(e1.getTransaction().bitcoinSerialize(), e2.getTransaction().bitcoinSerialize());
44 }
45 };
46
47 private BtcTransaction transaction;
48 private Long rskBlockNumber;
49 private Keccak256 rskTxHash;
50
51 public Entry(BtcTransaction transaction, Long rskBlockNumber, Keccak256 rskTxHash) {
52 this.transaction = transaction;
53 this.rskBlockNumber = rskBlockNumber;
54 this.rskTxHash = rskTxHash;
55 }
56
57 public Entry(BtcTransaction transaction, Long rskBlockNumber) { this(transaction, rskBlockNumber, null); }
58
59 public BtcTransaction getTransaction() {
60 return transaction;
61 }
62
63 public Long getRskBlockNumber() {
64 return rskBlockNumber;
65 }
66
67 public Keccak256 getRskTxHash() { return rskTxHash; }
68
69 @Override
70 public boolean equals(Object o) {
71 if (o == null || this.getClass() != o.getClass()) {
72 return false;
73 }
74
75 Entry otherEntry = (Entry) o;
76 return otherEntry.getTransaction().equals(getTransaction()) &&
77 otherEntry.getRskBlockNumber().equals(getRskBlockNumber()) &&
78 (otherEntry.getRskTxHash() == null && getRskTxHash() == null ||
79 otherEntry.getRskTxHash() != null && otherEntry.getRskTxHash().equals(getRskTxHash()));
80 }
81
82 @Override
83 public int hashCode() {
84 return Objects.hash(getTransaction(), getRskBlockNumber());
85 }
86 }
87
88 private Set<Entry> entries;
89
90 public ReleaseTransactionSet(Set<Entry> entries) {
91 this.entries = new HashSet<>(entries);
92 }
93
94 public Set<Entry> getEntriesWithoutHash() {
95 return entries.stream().filter(e -> e.getRskTxHash() == null).collect(Collectors.toSet());
96 }
97
98 public Set<Entry> getEntriesWithHash() {
99 return entries.stream().filter(e -> e.getRskTxHash() != null).collect(Collectors.toSet());
100 }
101
102 public Set<Entry> getEntries() {
103 return new HashSet<>(entries);
104 }
105
106 public void add(BtcTransaction transaction, Long blockNumber) {
107 // Disallow duplicate transactions
108 if (entries.stream().noneMatch(e -> e.getTransaction().equals(transaction))) {
109 entries.add(new Entry(transaction, blockNumber, null));
110 }
111 }
112
113 public void add(BtcTransaction transaction, Long blockNumber, Keccak256 rskTxHash) {
114 // Disallow duplicate transactions
115 if (entries.stream().noneMatch(e -> e.getTransaction().equals(transaction))) {
116 entries.add(new Entry(transaction, blockNumber, rskTxHash));
117 }
118 }
119
120 /**
121 * Given a block number and a minimum number of confirmations,
122 * returns a subset of transactions within the set that have
123 * at least that number of confirmations.
124 * Optionally supply a maximum slice size to limit the output
125 * size.
126 * Sliced items are also removed from the set (thus the name, slice).
127 * @param currentBlockNumber the current execution block number (height).
128 * @param minimumConfirmations the minimum desired confirmations for the slice elements.
129 * @param maximumSliceSize (optional) the maximum number of elements in the slice.
130 * @return the slice of entries.
131 */
132 public Set<Entry> sliceWithConfirmations(Long currentBlockNumber, Integer minimumConfirmations, Optional<Integer> maximumSliceSize) {
133 Set<Entry> output = new HashSet<>();
134
135 int count = 0;
136 Iterator<Entry> iterator = entries.iterator();
137 while (iterator.hasNext()) {
138 Entry entry = iterator.next();
139 if (hasEnoughConfirmations(entry, currentBlockNumber, minimumConfirmations) && (!maximumSliceSize.isPresent() || count < maximumSliceSize.get())) {
140 output.add(entry);
141 iterator.remove();
142 count++;
143 if (maximumSliceSize.isPresent() && count == maximumSliceSize.get()) {
144 break;
145 }
146 }
147 }
148
149 return output;
150 }
151
152 private boolean hasEnoughConfirmations(Entry entry, Long currentBlockNumber, Integer minimumConfirmations) {
153 return (currentBlockNumber - entry.getRskBlockNumber()) >= minimumConfirmations;
154 }
155 }