Coverage Summary for Class: ForkDetectionDataCalculator (co.rsk.mine)
Class |
Class, %
|
Method, %
|
Line, %
|
ForkDetectionDataCalculator |
0%
(0/1)
|
0%
(0/7)
|
0%
(0/40)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2019 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.mine;
20
21 import co.rsk.bitcoinj.core.Context;
22 import co.rsk.bitcoinj.core.NetworkParameters;
23 import co.rsk.bitcoinj.params.RegTestParams;
24 import co.rsk.core.types.ints.Uint8;
25 import org.ethereum.core.Block;
26 import org.ethereum.core.BlockHeader;
27
28 import java.nio.ByteBuffer;
29 import java.util.List;
30 import java.util.stream.Collectors;
31 import java.util.stream.IntStream;
32
33 public class ForkDetectionDataCalculator {
34
35 private static final int CPV_SIZE = 7;
36
37 private static final int CPV_JUMP_FACTOR = 64;
38
39 private static final int NUMBER_OF_UNCLES = 32;
40
41 // + 1 because genesis block can't be used since it does not contain a valid BTC header
42 private static final int MIN_MAINCHAIN_SIZE = CPV_SIZE * CPV_JUMP_FACTOR + 1;
43
44 private NetworkParameters params;
45
46 public ForkDetectionDataCalculator(){
47 this.params = RegTestParams.get();
48 new Context(params);
49 }
50
51 public byte[] calculate(List<Block> mainchainBlocks) {
52 if (mainchainBlocks.size() < MIN_MAINCHAIN_SIZE) {
53 return new byte[0];
54 }
55
56 List<BlockHeader> mainchainBlockHeaders = mainchainBlocks
57 .stream()
58 .map(Block::getHeader)
59 .collect(Collectors.toList());
60
61 return calculateWithBlockHeaders(mainchainBlockHeaders);
62 }
63
64 public byte[] calculateWithBlockHeaders(List<BlockHeader> mainchainBlockHeaders) {
65 if (mainchainBlockHeaders.size() < MIN_MAINCHAIN_SIZE) {
66 return new byte[0];
67 }
68
69 byte[] forkDetectionData = new byte[12];
70
71 byte[] commitToParentsVector = buildCommitToParentsVector(mainchainBlockHeaders);
72 System.arraycopy(commitToParentsVector, 0, forkDetectionData, 0, 7);
73
74 Uint8 numberOfUncles = getNumberOfUncles(mainchainBlockHeaders);
75 forkDetectionData[7] = numberOfUncles.asByte();
76
77 byte[] blockBeingMinedHeight = getBlockBeingMinedHeight(mainchainBlockHeaders);
78 System.arraycopy(blockBeingMinedHeight, 0, forkDetectionData, 8, 4);
79
80 return forkDetectionData;
81 }
82
83 private byte[] buildCommitToParentsVector(List<BlockHeader> mainchainBlocks) {
84 long bestBlockHeight = mainchainBlocks.get(0).getNumber();
85 long cpvStartHeight = (bestBlockHeight / CPV_JUMP_FACTOR) * CPV_JUMP_FACTOR;
86
87 byte[] commitToParentsVector = new byte[CPV_SIZE];
88 for(int i = 0; i < CPV_SIZE; i++){
89 long currentCpvElement = bestBlockHeight - cpvStartHeight + i * CPV_JUMP_FACTOR;
90 BlockHeader blockHeader = mainchainBlocks.get((int)currentCpvElement);
91 byte[] bitcoinBlock = blockHeader.getBitcoinMergedMiningHeader();
92
93 byte[] bitcoinBlockHash = params.getDefaultSerializer().makeBlock(bitcoinBlock).getHash().getBytes();
94 byte leastSignificantByte = bitcoinBlockHash[bitcoinBlockHash.length - 1];
95
96 commitToParentsVector[i] = leastSignificantByte;
97 }
98
99 return commitToParentsVector;
100 }
101
102 private Uint8 getNumberOfUncles(List<BlockHeader> mainchainBlocks) {
103 int sum = IntStream
104 .range(0, NUMBER_OF_UNCLES)
105 .map(i -> mainchainBlocks.get(i).getUncleCount()).sum();
106
107 final int maxUint = Uint8.MAX_VALUE.intValue();
108 if (sum > maxUint) {
109 return new Uint8(maxUint);
110 }
111
112 return new Uint8(sum);
113 }
114
115 private byte[] getBlockBeingMinedHeight(List<BlockHeader> mainchainBlocks) {
116 long blockBeingMinedHeight = mainchainBlocks.get(0).getNumber() + 1;
117 return ByteBuffer.allocate(4).putInt((int)blockBeingMinedHeight).array();
118 }
119 }