Coverage Summary for Class: ExecutionBlockRetriever (co.rsk.rpc)
Class |
Class, %
|
Method, %
|
Line, %
|
ExecutionBlockRetriever |
100%
(1/1)
|
75%
(3/4)
|
19.3%
(11/57)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2018 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.rpc;
20
21 import co.rsk.core.Coin;
22 import co.rsk.core.bc.BlockResult;
23 import co.rsk.core.bc.MiningMainchainView;
24 import co.rsk.mine.BlockToMineBuilder;
25 import co.rsk.mine.MinerServer;
26 import org.ethereum.core.Block;
27 import org.ethereum.core.BlockHeader;
28 import org.ethereum.core.Blockchain;
29 import org.ethereum.util.Utils;
30
31 import javax.annotation.Nullable;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Optional;
35
36 import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError;
37
38 /**
39 * Encapsulates the logic to retrieve or create an execution block
40 * for Web3 calls.
41 */
42 public class ExecutionBlockRetriever {
43 private static final String LATEST_ID = "latest";
44 private static final String PENDING_ID = "pending";
45
46 private final MiningMainchainView miningMainchainView;
47 private final Blockchain blockchain;
48 private final MinerServer minerServer;
49 private final BlockToMineBuilder builder;
50
51 @Nullable
52 private Block cachedBlock;
53 @Nullable
54 private BlockResult cachedResult;
55
56 public ExecutionBlockRetriever(MiningMainchainView miningMainchainView,
57 Blockchain blockchain,
58 MinerServer minerServer,
59 BlockToMineBuilder builder) {
60 this.miningMainchainView = miningMainchainView;
61 this.blockchain = blockchain;
62 this.minerServer = minerServer;
63 this.builder = builder;
64 }
65
66 @Deprecated
67 public BlockResult getExecutionBlock_workaround(String bnOrId) {
68 if (LATEST_ID.equals(bnOrId)) {
69 return newBlockResult_workaround(blockchain.getBestBlock());
70 }
71
72 if (PENDING_ID.equals(bnOrId)) {
73 Optional<Block> latestBlock = minerServer.getLatestBlock();
74 if (latestBlock.isPresent()) {
75 return newBlockResult_workaround(latestBlock.get());
76 }
77
78 Block bestBlock = blockchain.getBestBlock();
79 if (cachedBlock == null || !bestBlock.isParentOf(cachedBlock)) {
80
81 // If the miner server is not running there is no one to update the mining mainchain view,
82 // thus breaking eth_call with 'pending' parameter
83 //
84 // This is just a provisional fix not intended to remain in the long run
85 if (!minerServer.isRunning()) {
86 miningMainchainView.addBest(bestBlock.getHeader());
87 }
88
89 List<BlockHeader> mainchainHeaders = miningMainchainView.get();
90 cachedResult = builder.build(mainchainHeaders, null);
91 }
92
93 return cachedResult;
94 }
95
96 // Is the block specifier either a hexadecimal or decimal number?
97 Optional<Long> executionBlockNumber = Optional.empty();
98
99 if (Utils.isHexadecimalString(bnOrId)) {
100 executionBlockNumber = Optional.of(Utils.hexadecimalStringToLong(bnOrId));
101 } else if (Utils.isDecimalString(bnOrId)) {
102 executionBlockNumber = Optional.of(Utils.decimalStringToLong(bnOrId));
103 }
104
105 if (executionBlockNumber.isPresent()) {
106 Block executionBlock = blockchain.getBlockByNumber(executionBlockNumber.get());
107 if (executionBlock == null) {
108 throw invalidParamError(String.format("Invalid block number %d", executionBlockNumber.get()));
109 }
110 return newBlockResult_workaround(executionBlock);
111 }
112
113 // If we got here, the specifier given is unsupported
114 throw invalidParamError(String.format(
115 "Unsupported block specifier '%s'. Can only be either 'latest', " +
116 "'pending' or a specific block number (either hex - prepending '0x' or decimal).",
117 bnOrId));
118 }
119
120 public Block getExecutionBlock(String bnOrId) {
121 if (LATEST_ID.equals(bnOrId)) {
122 return blockchain.getBestBlock();
123 }
124
125 if (PENDING_ID.equals(bnOrId)) {
126 Optional<Block> latestBlock = minerServer.getLatestBlock();
127 if (latestBlock.isPresent()) {
128 return latestBlock.get();
129 }
130
131 Block bestBlock = blockchain.getBestBlock();
132 if (cachedBlock == null || !bestBlock.isParentOf(cachedBlock)) {
133
134 // If the miner server is not running there is no one to update the mining mainchain view,
135 // thus breaking eth_call with 'pending' parameter
136 //
137 // This is just a provisional fix not intended to remain in the long run
138 if (!minerServer.isRunning()) {
139 miningMainchainView.addBest(bestBlock.getHeader());
140 }
141
142 List<BlockHeader> mainchainHeaders = miningMainchainView.get();
143 cachedBlock = builder.build(mainchainHeaders, null).getBlock();
144 }
145
146 return cachedBlock;
147 }
148
149 // Is the block specifier either a hexadecimal or decimal number?
150 Optional<Long> executionBlockNumber = Optional.empty();
151
152 if (Utils.isHexadecimalString(bnOrId)) {
153 executionBlockNumber = Optional.of(Utils.hexadecimalStringToLong(bnOrId));
154 } else if (Utils.isDecimalString(bnOrId)) {
155 executionBlockNumber = Optional.of(Utils.decimalStringToLong(bnOrId));
156 }
157
158 if (executionBlockNumber.isPresent()) {
159 Block executionBlock = blockchain.getBlockByNumber(executionBlockNumber.get());
160 if (executionBlock == null) {
161 throw invalidParamError(String.format("Invalid block number %d", executionBlockNumber.get()));
162 }
163 return executionBlock;
164 }
165
166 // If we got here, the specifier given is unsupported
167 throw invalidParamError(String.format(
168 "Unsupported block specifier '%s'. Can only be either 'latest', " +
169 "'pending' or a specific block number (either hex - prepending '0x' or decimal).",
170 bnOrId));
171 }
172
173 @Deprecated
174 private static BlockResult newBlockResult_workaround(Block block) {
175 return new BlockResult(
176 block,
177 Collections.emptyList(),
178 Collections.emptyList(),
179 0,
180 Coin.ZERO,
181 null
182 );
183 }
184 }