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 }