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

Class Class, % Method, % Line, %
Federation 100% (1/1) 5.3% (1/19) 17% (9/53)


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.Address; 22 import co.rsk.bitcoinj.core.BtcECKey; 23 import co.rsk.bitcoinj.core.NetworkParameters; 24 import co.rsk.bitcoinj.script.Script; 25 import co.rsk.bitcoinj.script.ScriptBuilder; 26  27 import java.time.Instant; 28 import java.time.temporal.ChronoUnit; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Objects; 33 import java.util.stream.Collectors; 34  35 /** 36  * Immutable representation of an RSK Federation in the context of 37  * a specific BTC network. 38  * 39  * @author Ariel Mendelzon 40  */ 41  42 public class Federation { 43  protected final List<FederationMember> members; 44  protected final Instant creationTime; 45  protected final long creationBlockNumber; 46  protected final NetworkParameters btcParams; 47  48  protected Script redeemScript; 49  protected Script p2shScript; 50  protected Address address; 51  52  public Federation(List<FederationMember> members, Instant creationTime, long creationBlockNumber, NetworkParameters btcParams) { 53  // Sorting members ensures same order of federation members for same members 54  // Immutability provides protection against unwanted modification, thus making the Federation instance 55  // effectively immutable 56  this.members = Collections.unmodifiableList(members.stream().sorted(FederationMember.BTC_RSK_MST_PUBKEYS_COMPARATOR).collect(Collectors.toList())); 57  58  this.creationTime = creationTime.truncatedTo(ChronoUnit.MILLIS); 59  this.creationBlockNumber = creationBlockNumber; 60  this.btcParams = btcParams; 61  62  // Calculated once on-demand 63  this.redeemScript = null; 64  this.p2shScript = null; 65  this.address = null; 66  } 67  68  public List<FederationMember> getMembers() { 69  // Safe to return members since 70  // both list and instances are immutable 71  return members; 72  } 73  74  public List<BtcECKey> getBtcPublicKeys() { 75  // Copy instances since we don't control 76  // immutability of BtcECKey instances 77  return members.stream() 78  .map(m -> m.getBtcPublicKey().getPubKey()) 79  .map(BtcECKey::fromPublicOnly) 80  .collect(Collectors.toList()); 81  } 82  83  public int getNumberOfSignaturesRequired() { 84  return members.size() / 2 + 1; 85  } 86  87  public Instant getCreationTime() { 88  return creationTime; 89  } 90  91  public NetworkParameters getBtcParams() { 92  return btcParams; 93  } 94  95  public long getCreationBlockNumber() { 96  return creationBlockNumber; 97  } 98  99  public Script getRedeemScript() { 100  if (redeemScript == null) { 101  redeemScript = ScriptBuilder.createRedeemScript(getNumberOfSignaturesRequired(), getBtcPublicKeys()); 102  } 103  104  return redeemScript; 105  } 106  107  public Script getStandardRedeemScript() { 108  return getRedeemScript(); 109  } 110  111  public Script getP2SHScript() { 112  if (p2shScript == null) { 113  p2shScript = ScriptBuilder.createP2SHOutputScript(getNumberOfSignaturesRequired(), getBtcPublicKeys()); 114  } 115  116  return p2shScript; 117  } 118  119  public Address getAddress() { 120  if (address == null) { 121  address = Address.fromP2SHScript(btcParams, getP2SHScript()); 122  } 123  124  return address; 125  } 126  127  public int getSize() { 128  return members.size(); 129  } 130  131  public Integer getBtcPublicKeyIndex(BtcECKey key) { 132  for (int i = 0; i < members.size(); i++) { 133  // note that this comparison doesn't take into account 134  // key compression 135  if (Arrays.equals(key.getPubKey(), members.get(i).getBtcPublicKey().getPubKey())) { 136  return i; 137  } 138  } 139  140  return null; 141  } 142  143  public boolean hasBtcPublicKey(BtcECKey key) { 144  return getBtcPublicKeyIndex(key) != null; 145  } 146  147  public boolean hasMemberWithRskAddress(byte[] address) { 148  return members.stream() 149  .anyMatch(m -> Arrays.equals(m.getRskPublicKey().getAddress(), address)); 150  } 151  152  public boolean isMember(FederationMember federationMember){ 153  return this.members.contains(federationMember); 154  } 155  156  @Override 157  public String toString() { 158  return String.format("%d of %d signatures federation", getNumberOfSignaturesRequired(), members.size()); 159  } 160  161  @Override 162  public boolean equals(Object other) { 163  if (this == other) { 164  return true; 165  } 166  167  if (other == null || this.getClass() != other.getClass()) { 168  return false; 169  } 170  171  Federation otherFederation = (Federation) other; 172  173  return this.getNumberOfSignaturesRequired() == otherFederation.getNumberOfSignaturesRequired() && 174  this.getSize() == otherFederation.getSize() && 175  this.getCreationTime().equals(otherFederation.getCreationTime()) && 176  this.creationBlockNumber == otherFederation.creationBlockNumber && 177  this.btcParams.equals(otherFederation.btcParams) && 178  this.members.equals(otherFederation.members); 179  } 180  181  @Override 182  public int hashCode() { 183  // Can use java.util.Objects.hash since all of Instant, int and List<BtcECKey> have 184  // well-defined hashCode()s 185  return Objects.hash( 186  getCreationTime(), 187  this.creationBlockNumber, 188  getNumberOfSignaturesRequired(), 189  getBtcPublicKeys() 190  ); 191  } 192 }