Coverage Summary for Class: BlockFactory (org.ethereum.core)
Class |
Class, %
|
Method, %
|
Line, %
|
BlockFactory |
100%
(1/1)
|
25%
(3/12)
|
6.8%
(6/88)
|
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 org.ethereum.core;
20
21 import co.rsk.config.MiningConfig;
22 import co.rsk.core.BlockDifficulty;
23 import co.rsk.core.Coin;
24 import co.rsk.core.RskAddress;
25 import co.rsk.remasc.RemascTransaction;
26 import org.bouncycastle.util.BigIntegers;
27 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
28 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
29 import org.ethereum.util.RLP;
30 import org.ethereum.util.RLPElement;
31 import org.ethereum.util.RLPList;
32
33 import java.math.BigInteger;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37
38 import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
39
40 public class BlockFactory {
41 private static final int RLP_HEADER_SIZE = 17;
42 private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 20;
43
44 private final ActivationConfig activationConfig;
45
46 public BlockFactory(ActivationConfig activationConfig) {
47 this.activationConfig = activationConfig;
48 }
49
50 public BlockHeaderBuilder getBlockHeaderBuilder() {
51 return new BlockHeaderBuilder(activationConfig);
52 }
53
54 public Block cloneBlockForModification(Block block) {
55 return decodeBlock(block.getEncoded(), false);
56 }
57
58 public Block decodeBlock(byte[] rawData) {
59 return decodeBlock(rawData, true);
60 }
61
62 private Block decodeBlock(byte[] rawData, boolean sealed) {
63 RLPList block = RLP.decodeList(rawData);
64 if (block.size() != 3) {
65 throw new IllegalArgumentException("A block must have 3 exactly items");
66 }
67
68 RLPList rlpHeader = (RLPList) block.get(0);
69 BlockHeader header = decodeHeader(rlpHeader, sealed);
70
71 List<Transaction> transactionList = parseTxs((RLPList) block.get(1));
72
73 RLPList uncleHeadersRlp = (RLPList) block.get(2);
74
75 List<BlockHeader> uncleList = new ArrayList<>();
76
77 for (int k = 0; k < uncleHeadersRlp.size(); k++) {
78 RLPElement element = uncleHeadersRlp.get(k);
79 BlockHeader uncleHeader = decodeHeader((RLPList)element, sealed);
80 uncleList.add(uncleHeader);
81 }
82
83 return newBlock(header, transactionList, uncleList, sealed);
84 }
85
86 public Block newBlock(BlockHeader header, List<Transaction> transactionList, List<BlockHeader> uncleList) {
87 return newBlock(header, transactionList, uncleList, true);
88 }
89
90 public Block newBlock(BlockHeader header, List<Transaction> transactionList, List<BlockHeader> uncleList, boolean sealed) {
91 boolean isRskip126Enabled = activationConfig.isActive(ConsensusRule.RSKIP126, header.getNumber());
92 return new Block(header, transactionList, uncleList, isRskip126Enabled, sealed);
93 }
94
95 public BlockHeader decodeHeader(byte[] encoded) {
96 return decodeHeader(RLP.decodeList(encoded), true);
97 }
98
99 private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) {
100 byte[] parentHash = rlpHeader.get(0).getRLPData();
101 byte[] unclesHash = rlpHeader.get(1).getRLPData();
102 byte[] coinBaseBytes = rlpHeader.get(2).getRLPData();
103 RskAddress coinbase = RLP.parseRskAddress(coinBaseBytes);
104 byte[] stateRoot = rlpHeader.get(3).getRLPData();
105 if (stateRoot == null) {
106 stateRoot = EMPTY_TRIE_HASH;
107 }
108
109 byte[] txTrieRoot = rlpHeader.get(4).getRLPData();
110 if (txTrieRoot == null) {
111 txTrieRoot = EMPTY_TRIE_HASH;
112 }
113
114 byte[] receiptTrieRoot = rlpHeader.get(5).getRLPData();
115 if (receiptTrieRoot == null) {
116 receiptTrieRoot = EMPTY_TRIE_HASH;
117 }
118
119 byte[] logsBloom = rlpHeader.get(6).getRLPData();
120 byte[] difficultyBytes = rlpHeader.get(7).getRLPData();
121 BlockDifficulty difficulty = RLP.parseBlockDifficulty(difficultyBytes);
122
123 byte[] nrBytes = rlpHeader.get(8).getRLPData();
124 byte[] glBytes = rlpHeader.get(9).getRLPData();
125 byte[] guBytes = rlpHeader.get(10).getRLPData();
126 byte[] tsBytes = rlpHeader.get(11).getRLPData();
127
128 long blockNumber = parseBigInteger(nrBytes).longValueExact();
129
130 long gasUsed = parseBigInteger(guBytes).longValueExact();
131 long timestamp = parseBigInteger(tsBytes).longValueExact();
132
133 byte[] extraData = rlpHeader.get(12).getRLPData();
134
135 Coin paidFees = RLP.parseCoin(rlpHeader.get(13).getRLPData());
136 byte[] minimumGasPriceBytes = rlpHeader.get(14).getRLPData();
137 Coin minimumGasPrice = RLP.parseSignedCoinNonNullZero(minimumGasPriceBytes);
138
139 if (!canBeDecoded(rlpHeader, blockNumber)) {
140 throw new IllegalArgumentException(String.format(
141 "A block header must have 16/17 elements or 19/20 including merged-mining fields but it had %d",
142 rlpHeader.size()
143 ));
144 }
145
146 int r = 15;
147
148 boolean isUmm = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber);
149
150 boolean includeUncleCount = isUmm ||
151 // sizes prior to UMM activation
152 rlpHeader.size() == (RLP_HEADER_SIZE-1) || rlpHeader.size() == (RLP_HEADER_SIZE_WITH_MERGED_MINING-1);
153
154 int uncleCount = 0;
155 if (includeUncleCount) {
156 byte[] ucBytes = rlpHeader.get(r++).getRLPData();
157 uncleCount = parseBigInteger(ucBytes).intValueExact();
158 }
159
160 byte[] ummRoot = null;
161 if (isUmm) {
162 ummRoot = rlpHeader.get(r++).getRLPRawData();
163 }
164
165 byte[] bitcoinMergedMiningHeader = null;
166 byte[] bitcoinMergedMiningMerkleProof = null;
167 byte[] bitcoinMergedMiningCoinbaseTransaction = null;
168 if (rlpHeader.size() > r) {
169 bitcoinMergedMiningHeader = rlpHeader.get(r++).getRLPData();
170 bitcoinMergedMiningMerkleProof = rlpHeader.get(r++).getRLPRawData();
171 bitcoinMergedMiningCoinbaseTransaction = rlpHeader.get(r++).getRLPData();
172 }
173
174 boolean useRskip92Encoding = activationConfig.isActive(ConsensusRule.RSKIP92, blockNumber);
175 boolean includeForkDetectionData = activationConfig.isActive(ConsensusRule.RSKIP110, blockNumber) &&
176 blockNumber >= MiningConfig.REQUIRED_NUMBER_OF_BLOCKS_FOR_FORK_DETECTION_CALCULATION;
177
178 if (blockNumber == Genesis.NUMBER) {
179 return new GenesisHeader(
180 parentHash,
181 unclesHash,
182 logsBloom,
183 difficultyBytes,
184 blockNumber,
185 glBytes,
186 gasUsed,
187 timestamp,
188 extraData,
189 bitcoinMergedMiningHeader,
190 bitcoinMergedMiningMerkleProof,
191 bitcoinMergedMiningCoinbaseTransaction,
192 minimumGasPriceBytes,
193 useRskip92Encoding,
194 coinBaseBytes,
195 stateRoot);
196 }
197
198 return new BlockHeader(
199 parentHash, unclesHash, coinbase, stateRoot,
200 txTrieRoot, receiptTrieRoot, logsBloom, difficulty,
201 blockNumber, glBytes, gasUsed, timestamp, extraData,
202 paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof,
203 bitcoinMergedMiningCoinbaseTransaction, new byte[0],
204 minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData,
205 ummRoot
206 );
207 }
208
209 private boolean canBeDecoded(RLPList rlpHeader, long blockNumber) {
210 int preUmmHeaderSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber) ? 0 : 1;
211
212 return rlpHeader.size() == (RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment) ||
213 rlpHeader.size() == (RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment);
214 }
215
216 private static BigInteger parseBigInteger(byte[] bytes) {
217 return bytes == null ? BigInteger.ZERO : BigIntegers.fromUnsignedByteArray(bytes);
218 }
219
220 private static List<Transaction> parseTxs(RLPList txTransactions) {
221 List<Transaction> parsedTxs = new ArrayList<>();
222
223 for (int i = 0; i < txTransactions.size(); i++) {
224 RLPElement transactionRaw = txTransactions.get(i);
225 Transaction tx = new ImmutableTransaction(transactionRaw.getRLPData());
226
227 if (tx.isRemascTransaction(i, txTransactions.size())) {
228 // It is the remasc transaction
229 tx = new RemascTransaction(transactionRaw.getRLPData());
230 }
231 parsedTxs.add(tx);
232 }
233
234 return Collections.unmodifiableList(parsedTxs);
235 }
236 }