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 }