Coverage Summary for Class: FamilyUtils (co.rsk.core.bc)

Class Class, % Method, % Line, %
FamilyUtils 100% (1/1) 36.4% (4/11) 31% (18/58)


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.core.bc; 20  21 import co.rsk.crypto.Keccak256; 22 import org.ethereum.core.Block; 23 import org.ethereum.core.BlockHeader; 24 import org.ethereum.db.BlockStore; 25  26 import javax.annotation.Nonnull; 27 import java.util.ArrayList; 28 import java.util.HashSet; 29 import java.util.List; 30 import java.util.Set; 31 import java.util.stream.Collectors; 32  33 import static java.lang.Math.max; 34  35 /** 36  * Created by ajlopez on 12/08/2016. 37  */ 38 public class FamilyUtils { 39  40  /** 41  * Calculate the set of hashes of ancestors of a block 42  * 43  * @param blockStore the block store to use 44  * @param block the block to use 45  * @param limitNum maximum number of ancestors to retrieve 46  * 47  * @return set of ancestors block hashes 48  */ 49  public static Set<Keccak256> getAncestors(BlockStore blockStore, Block block, int limitNum) { 50  return getAncestors(blockStore, block.getNumber(), block.getParentHash(), limitNum); 51  } 52  53  public static Set<Keccak256> getAncestors(BlockStore blockStore, long blockNumber, Keccak256 parentHash, int limitNum) { 54  Set<Keccak256> ret = new HashSet<>(); 55  56  if (blockStore == null) { 57  return ret; 58  } 59  60  int limit = (int) max(0, blockNumber - limitNum); 61  Block it = blockStore.getBlockByHash(parentHash.getBytes()); 62  63  while(it != null && it.getNumber() >= limit) { 64  ret.add(it.getHash()); 65  it = blockStore.getBlockByHash(it.getParentHash().getBytes()); 66  } 67  68  return ret; 69  } 70  71  /** 72  * Calculate the set of already used hashes in the chain of a block 73  * 74  * @param blockStore the block store to use 75  * @param block the block to use 76  * @param limitNum maximum number of ancestors to examine 77  * 78  * @return set of already used uncles block hashes 79  */ 80  public static Set<Keccak256> getUsedUncles(BlockStore blockStore, Block block, int limitNum) { 81  return getUsedUncles(blockStore, block.getNumber(), block.getParentHash(), limitNum); 82  } 83  84  public static Set<Keccak256> getUsedUncles(BlockStore blockStore, long blockNumber, Keccak256 parentHash, int limitNum) { 85  Set<Keccak256> ret = new HashSet<>(); 86  87  if (blockStore == null) { 88  return ret; 89  } 90  91  long minNumber = max(0, blockNumber - limitNum); 92  Block it = blockStore.getBlockByHash(parentHash.getBytes()); 93  94  while(it != null && it.getNumber() >= minNumber) { 95  for (BlockHeader uncle : it.getUncleList()) { 96  ret.add(uncle.getHash()); 97  } 98  it = blockStore.getBlockByHash(it.getParentHash().getBytes()); 99  } 100  101  return ret; 102  } 103  104  public static List<BlockHeader> getUnclesHeaders(BlockStore store, Block block, int levels) { 105  return getUnclesHeaders(store, block.getNumber(), block.getParentHash(), levels); 106  } 107  108  public static List<BlockHeader> getUnclesHeaders(@Nonnull BlockStore store, long blockNumber, Keccak256 parentHash, int levels) { 109  List<BlockHeader> uncles = new ArrayList<>(); 110  Set<Keccak256> unclesHeaders = getUncles(store, blockNumber, parentHash, levels); 111  112  for (Keccak256 uncleHash : unclesHeaders) { 113  Block uncle = store.getBlockByHash(uncleHash.getBytes()); 114  115  if (uncle != null) { 116  uncles.add(uncle.getHeader()); 117  } 118  } 119  120  return uncles; 121  } 122  123  public static Set<Keccak256> getUncles(BlockStore store, Block block, int levels) { 124  return getUncles(store, block.getNumber(), block.getParentHash(), levels); 125  } 126  127  public static Set<Keccak256> getUncles(BlockStore store, long blockNumber, Keccak256 parentHash, int levels) { 128  Set<Keccak256> family = getFamily(store, blockNumber, parentHash, levels); 129  Set<Keccak256> ancestors = getAncestors(store, blockNumber, parentHash, levels); 130  family.removeAll(ancestors); 131  family.removeAll(getUsedUncles(store, blockNumber, parentHash, levels)); 132  133  return family; 134  } 135  136  public static Set<Keccak256> getFamily(BlockStore store, Block block, int levels) { 137  return getFamily(store, block.getNumber(), block.getParentHash(), levels); 138  } 139  140  public static Set<Keccak256> getFamily(BlockStore store, long blockNumber, Keccak256 parentHash, int levels) { 141  long minNumber = max(0, blockNumber - levels); 142  143  List<Block> ancestors = new ArrayList<>(); 144  Block parent = store.getBlockByHash(parentHash.getBytes()); 145  146  while (parent != null && parent.getNumber() >= minNumber) { 147  ancestors.add(0, parent); 148  parent = store.getBlockByHash(parent.getParentHash().getBytes()); 149  } 150  151  Set<Keccak256> family = ancestors.stream().map(Block::getHash).collect(Collectors.toSet()); 152  153  for (int k = 1; k < ancestors.size(); k++) { 154  Block ancestorParent = ancestors.get(k - 1); 155  Block ancestor = ancestors.get(k); 156  List<Block> uncles = store.getChainBlocksByNumber(ancestor.getNumber()); 157  158  for (Block uncle : uncles) { 159  if (!ancestorParent.getHash().equals(uncle.getParentHash())) { 160  continue; 161  } 162  163  if (ancestor.getHash().equals(uncle.getHash())) { 164  continue; 165  } 166  167  family.add(uncle.getHash()); 168  } 169  } 170  171  return family; 172  } 173 }