Coverage Summary for Class: EthModule (co.rsk.rpc.modules.eth)
Class |
Method, %
|
Line, %
|
EthModule |
60%
(9/15)
|
52%
(51/98)
|
EthModule$MockitoMock$1744992607 |
EthModule$MockitoMock$1744992607$auxiliary$6xtQiiXG |
EthModule$MockitoMock$1744992607$auxiliary$FV70tSan |
EthModule$MockitoMock$1744992607$auxiliary$jgXUBRZm |
EthModule$MockitoMock$1744992607$auxiliary$JkJzDD7y |
EthModule$MockitoMock$1744992607$auxiliary$lYwD2dBu |
EthModule$MockitoMock$1744992607$auxiliary$oKyLZRQA |
EthModule$MockitoMock$1744992607$auxiliary$Qqlo8iPe |
EthModule$MockitoMock$1744992607$auxiliary$SC1RXQmV |
EthModule$MockitoMock$1744992607$auxiliary$uhQbvw0M |
EthModule$MockitoMock$1744992607$auxiliary$Xi0IrHwL |
EthModule$MockitoMock$1744992607$auxiliary$YgAuK9ew |
Total |
60%
(9/15)
|
52%
(51/98)
|
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.rpc.modules.eth;
20
21 import co.rsk.bitcoinj.store.BlockStoreException;
22 import co.rsk.config.BridgeConstants;
23 import co.rsk.core.ReversibleTransactionExecutor;
24 import co.rsk.core.RskAddress;
25 import co.rsk.core.bc.AccountInformationProvider;
26 import co.rsk.core.bc.BlockResult;
27 import co.rsk.db.RepositoryLocator;
28 import co.rsk.peg.BridgeState;
29 import co.rsk.peg.BridgeSupport;
30 import co.rsk.peg.BridgeSupportFactory;
31 import co.rsk.rpc.ExecutionBlockRetriever;
32 import co.rsk.trie.TrieStoreImpl;
33 import org.ethereum.core.*;
34 import org.ethereum.datasource.HashMapDB;
35 import org.ethereum.db.MutableRepository;
36 import org.ethereum.rpc.TypeConverter;
37 import org.ethereum.rpc.Web3;
38 import org.ethereum.rpc.converters.CallArgumentsToByteArray;
39 import org.ethereum.rpc.exception.RskJsonRpcRequestException;
40 import org.ethereum.vm.PrecompiledContracts;
41 import org.ethereum.vm.program.ProgramResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import java.io.IOException;
46 import java.util.Arrays;
47 import java.util.Map;
48 import java.util.Optional;
49
50 import static java.util.Arrays.copyOfRange;
51 import static org.ethereum.rpc.TypeConverter.stringHexToBigInteger;
52 import static org.ethereum.rpc.TypeConverter.toUnformattedJsonHex;
53 import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError;
54
55 // TODO add all RPC methods
56 public class EthModule
57 implements EthModuleWallet, EthModuleTransaction {
58
59 private static final Logger LOGGER = LoggerFactory.getLogger("web3");
60
61 private static final CallTransaction.Function ERROR_ABI_FUNCTION = CallTransaction.Function.fromSignature("Error", "string");
62 private static final byte[] ERROR_ABI_FUNCTION_SIGNATURE = ERROR_ABI_FUNCTION.encodeSignature(); //08c379a0
63
64 private final Blockchain blockchain;
65 private final TransactionPool transactionPool;
66 private final ReversibleTransactionExecutor reversibleTransactionExecutor;
67 private final ExecutionBlockRetriever executionBlockRetriever;
68 private final RepositoryLocator repositoryLocator;
69 private final EthModuleWallet ethModuleWallet;
70 private final EthModuleTransaction ethModuleTransaction;
71 private final BridgeConstants bridgeConstants;
72 private final BridgeSupportFactory bridgeSupportFactory;
73 private final byte chainId;
74
75
76 public EthModule(
77 BridgeConstants bridgeConstants,
78 byte chainId,
79 Blockchain blockchain,
80 TransactionPool transactionPool,
81 ReversibleTransactionExecutor reversibleTransactionExecutor,
82 ExecutionBlockRetriever executionBlockRetriever,
83 RepositoryLocator repositoryLocator,
84 EthModuleWallet ethModuleWallet,
85 EthModuleTransaction ethModuleTransaction,
86 BridgeSupportFactory bridgeSupportFactory) {
87 this.chainId = chainId;
88 this.blockchain = blockchain;
89 this.transactionPool = transactionPool;
90 this.reversibleTransactionExecutor = reversibleTransactionExecutor;
91 this.executionBlockRetriever = executionBlockRetriever;
92 this.repositoryLocator = repositoryLocator;
93 this.ethModuleWallet = ethModuleWallet;
94 this.ethModuleTransaction = ethModuleTransaction;
95 this.bridgeConstants = bridgeConstants;
96 this.bridgeSupportFactory = bridgeSupportFactory;
97 }
98
99 @Override
100 public String[] accounts() {
101 return ethModuleWallet.accounts();
102 }
103
104 public Map<String, Object> bridgeState() throws IOException, BlockStoreException {
105 Block bestBlock = blockchain.getBestBlock();
106 Repository track = repositoryLocator.startTrackingAt(bestBlock.getHeader());
107
108 BridgeSupport bridgeSupport = bridgeSupportFactory.newInstance(
109 track, bestBlock, PrecompiledContracts.BRIDGE_ADDR, null);
110
111 byte[] result = bridgeSupport.getStateForDebugging();
112
113 BridgeState state = BridgeState.create(bridgeConstants, result, null);
114
115 return state.stateToMap();
116 }
117
118 public String call(Web3.CallArguments args, String bnOrId) {
119 String hReturn = null;
120 try {
121 BlockResult blockResult = executionBlockRetriever.getExecutionBlock_workaround(bnOrId);
122 ProgramResult res;
123 if (blockResult.getFinalState() != null) {
124 res = callConstant_workaround(args, blockResult);
125 } else {
126 res = callConstant(args, blockResult.getBlock());
127 }
128
129 if (res.isRevert()) {
130 Optional<String> revertReason = decodeRevertReason(res);
131 if (revertReason.isPresent()) {
132 throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertReason.get());
133 } else {
134 throw RskJsonRpcRequestException.transactionRevertedExecutionError();
135 }
136 }
137
138 hReturn = toUnformattedJsonHex(res.getHReturn());
139
140 return hReturn;
141 } finally {
142 LOGGER.debug("eth_call(): {}", hReturn);
143 }
144 }
145
146 public String estimateGas(Web3.CallArguments args) {
147 String s = null;
148 try {
149 ProgramResult res = callConstant(args, blockchain.getBestBlock());
150 return s = TypeConverter.toQuantityJsonHex(res.getGasUsed());
151 } finally {
152 LOGGER.debug("eth_estimateGas(): {}", s);
153 }
154 }
155
156 @Override
157 public String sendTransaction(Web3.CallArguments args) {
158 return ethModuleTransaction.sendTransaction(args);
159 }
160
161 @Override
162 public String sendRawTransaction(String rawData) {
163 return ethModuleTransaction.sendRawTransaction(rawData);
164 }
165
166 @Override
167 public String sign(String addr, String data) {
168 return ethModuleWallet.sign(addr, data);
169 }
170
171 public String chainId() {
172 return TypeConverter.toJsonHex(new byte[] { chainId });
173 }
174
175 public String getCode(String address, String blockId) {
176 if (blockId == null) {
177 throw new NullPointerException();
178 }
179
180 String s = null;
181 try {
182 RskAddress addr = new RskAddress(address);
183
184 AccountInformationProvider accountInformationProvider = getAccountInformationProvider(blockId);
185
186 if(accountInformationProvider != null) {
187 byte[] code = accountInformationProvider.getCode(addr);
188
189 // Code can be null, if there is no account.
190 if (code == null) {
191 code = new byte[0];
192 }
193
194 s = toUnformattedJsonHex(code);
195 }
196
197 return s;
198 } finally {
199 if (LOGGER.isDebugEnabled()) {
200 LOGGER.debug("eth_getCode({}, {}): {}", address, blockId, s);
201 }
202 }
203 }
204
205 private AccountInformationProvider getAccountInformationProvider(String id) {
206 switch (id.toLowerCase()) {
207 case "pending":
208 return transactionPool.getPendingState();
209 case "earliest":
210 return repositoryLocator.snapshotAt(blockchain.getBlockByNumber(0).getHeader());
211 case "latest":
212 return repositoryLocator.snapshotAt(blockchain.getBestBlock().getHeader());
213 default:
214 try {
215 long blockNumber = stringHexToBigInteger(id).longValue();
216 Block requestedBlock = blockchain.getBlockByNumber(blockNumber);
217 if (requestedBlock != null) {
218 return repositoryLocator.snapshotAt(requestedBlock.getHeader());
219 }
220 return null;
221 } catch (NumberFormatException | StringIndexOutOfBoundsException e) {
222 throw invalidParamError("invalid blocknumber " + id);
223 }
224 }
225 }
226
227 private ProgramResult callConstant(Web3.CallArguments args, Block executionBlock) {
228 CallArgumentsToByteArray hexArgs = new CallArgumentsToByteArray(args);
229 return reversibleTransactionExecutor.executeTransaction(
230 executionBlock,
231 executionBlock.getCoinbase(),
232 hexArgs.getGasPrice(),
233 hexArgs.getGasLimit(),
234 hexArgs.getToAddress(),
235 hexArgs.getValue(),
236 hexArgs.getData(),
237 hexArgs.getFromAddress()
238 );
239 }
240
241 /**
242 * Look for { Error("msg") } function, if it matches decode the "msg" param.
243 * The 4 first bytes are the function signature.
244 *
245 * @param res
246 * @return revert reason, empty if didnt match.
247 */
248 public static Optional<String> decodeRevertReason(ProgramResult res) {
249 byte[] bytes = res.getHReturn();
250 if (bytes == null || bytes.length < 4) {
251 return Optional.empty();
252 }
253
254 final byte[] signature = copyOfRange(res.getHReturn(), 0, 4);
255 if (!Arrays.equals(signature, ERROR_ABI_FUNCTION_SIGNATURE)) {
256 return Optional.empty();
257 }
258
259 final Object[] decode = ERROR_ABI_FUNCTION.decode(res.getHReturn());
260 return decode != null && decode.length > 0 ? Optional.of((String) decode[0]) : Optional.empty();
261 }
262
263 @Deprecated
264 private ProgramResult callConstant_workaround(Web3.CallArguments args, BlockResult executionBlock) {
265 CallArgumentsToByteArray hexArgs = new CallArgumentsToByteArray(args);
266 return reversibleTransactionExecutor.executeTransaction_workaround(
267 new MutableRepository(new TrieStoreImpl(new HashMapDB()), executionBlock.getFinalState()),
268 executionBlock.getBlock(),
269 executionBlock.getBlock().getCoinbase(),
270 hexArgs.getGasPrice(),
271 hexArgs.getGasLimit(),
272 hexArgs.getToAddress(),
273 hexArgs.getValue(),
274 hexArgs.getData(),
275 hexArgs.getFromAddress()
276 );
277 }
278 }