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 }