Coverage Summary for Class: TxPoolModuleImpl (co.rsk.rpc.modules.txpool)

Class Class, % Method, % Line, %
TxPoolModuleImpl 100% (1/1) 12.5% (1/8) 5.6% (4/71)


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.modules.txpool; 20  21 import co.rsk.core.RskAddress; 22 import com.fasterxml.jackson.databind.JsonNode; 23 import com.fasterxml.jackson.databind.node.ArrayNode; 24 import com.fasterxml.jackson.databind.node.JsonNodeFactory; 25 import com.fasterxml.jackson.databind.node.ObjectNode; 26 import org.ethereum.core.Transaction; 27 import org.ethereum.core.TransactionPool; 28 import org.ethereum.rpc.TypeConverter; 29  30 import java.math.BigInteger; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.function.Function; 36  37 public class TxPoolModuleImpl implements TxPoolModule { 38  39  public static final String PENDING = "pending"; 40  public static final String QUEUED = "queued"; 41  private final JsonNodeFactory jsonNodeFactory; 42  private final TransactionPool transactionPool; 43  44  public TxPoolModuleImpl(TransactionPool transactionPool) { 45  this.transactionPool = transactionPool; 46  jsonNodeFactory = JsonNodeFactory.instance; 47  } 48  49  /** 50  * This method should return 2 dictionaries containing pending and queued transactions 51  * Each entry is an origin-address to a batch of scheduled transactions 52  * These batches themselves are maps associating nonces with actual transactions. 53  * When there are no transactions the answer would be 54  * "{"pending": {}, "queued": {}}" 55  */ 56  @Override 57  public JsonNode content() { 58  Map<String, JsonNode> contentProps = new HashMap<>(); 59  Map<RskAddress, Map<BigInteger, List<Transaction>>> pendingGrouped = groupTransactions(transactionPool.getPendingTransactions()); 60  Map<RskAddress, Map<BigInteger, List<Transaction>>> queuedGrouped = groupTransactions(transactionPool.getQueuedTransactions()); 61  contentProps.put(PENDING, serializeTransactions(pendingGrouped, this::fullSerializer)); 62  contentProps.put(QUEUED, serializeTransactions(queuedGrouped, this::fullSerializer)); 63  JsonNode node = jsonNodeFactory.objectNode().setAll(contentProps); 64  return node; 65  } 66  67  private JsonNode serializeTransactions( 68  Map<RskAddress, Map<BigInteger, List<Transaction>>> groupedTransactions, 69  Function<Transaction, JsonNode> txSerializer) { 70  Map<String, JsonNode> senderProps = new HashMap<>(); 71  for (Map.Entry<RskAddress, Map<BigInteger, List<Transaction>>> entrySender : groupedTransactions.entrySet()){ 72  Map<String, JsonNode> nonceProps = new HashMap<>(); 73  for (Map.Entry<BigInteger, List<Transaction>> entryNonce : entrySender.getValue().entrySet()){ 74  ArrayNode txsNodes = jsonNodeFactory.arrayNode(); 75  for (Transaction tx : entryNonce.getValue()) { 76  txsNodes.add(txSerializer.apply(tx)); 77  } 78  nonceProps.put(entryNonce.getKey().toString(),txsNodes); 79  } 80  senderProps.put(entrySender.getKey().toString(), jsonNodeFactory.objectNode().setAll(nonceProps)); 81  } 82  return jsonNodeFactory.objectNode().setAll(senderProps); 83  } 84  85  private JsonNode fullSerializer(Transaction tx) { 86  ObjectNode txNode = jsonNodeFactory.objectNode(); 87  88  txNode.put("blockHash", "0x0000000000000000000000000000000000000000000000000000000000000000"); 89  txNode.putNull("blockNumber"); 90  txNode.putNull("transactionIndex"); 91  92  txNode.put("from", TypeConverter.toJsonHex(tx.getSender().getBytes())); 93  txNode.put("gas", TypeConverter.toQuantityJsonHex(tx.getGasLimitAsInteger())); 94  txNode.put("gasPrice", TypeConverter.toJsonHex(tx.getGasPrice().getBytes())); 95  txNode.put("hash", TypeConverter.toJsonHex(tx.getHash().toHexString())); 96  txNode.put("input", TypeConverter.toUnformattedJsonHex(tx.getData())); 97  txNode.put("nonce", TypeConverter.toQuantityJsonHex(tx.getNonceAsInteger())); 98  txNode.put("to", TypeConverter.toJsonHex(tx.getReceiveAddress().getBytes())); 99  txNode.put("value", TypeConverter.toJsonHex(tx.getValue().getBytes())); 100  101  return txNode; 102  } 103  104  private JsonNode summarySerializer(Transaction tx) { 105  String summary = "{}: {} wei + {} x {} gas"; 106  String summaryFormatted = String.format(summary, 107  tx.getReceiveAddress().toString(), 108  tx.getValue().toString(), 109  tx.getGasLimitAsInteger().toString(), 110  tx.getGasPrice().toString()); 111  return jsonNodeFactory.textNode(summaryFormatted); 112  } 113  114  private Map<RskAddress, Map<BigInteger, List<Transaction>>> groupTransactions(List<Transaction> transactions) { 115  Map<RskAddress, Map<BigInteger, List<Transaction>>> groupedTransactions = new HashMap<>(); 116  for (Transaction tx : transactions){ 117  Map<BigInteger, List<Transaction>> txsBySender = groupedTransactions.get(tx.getSender()); 118  if (txsBySender == null){ 119  txsBySender = new HashMap<>(); 120  groupedTransactions.put(tx.getSender(), txsBySender); 121  } 122  List<Transaction> txsByNonce = txsBySender.get(tx.getNonceAsInteger()); 123  if (txsByNonce == null){ 124  List<Transaction> txs = new ArrayList<>(); 125  txs.add(tx); 126  txsBySender.put(tx.getNonceAsInteger(), txs); 127  } else { 128  txsByNonce.add(tx); 129  } 130  } 131  return groupedTransactions; 132  } 133  134  /** 135  * This method should return 2 dictionaries containing pending and queued transactions 136  * Each entry is an origin-address to a batch of scheduled transactions 137  * These batches themselves are maps associating nonces with transactions summary strings. 138  * When there are no transactions the answer would be 139  * "{"pending": {}, "queued": {}}" 140  */ 141  @Override 142  public JsonNode inspect() { 143  Map<String, JsonNode> contentProps = new HashMap<>(); 144  Map<RskAddress, Map<BigInteger, List<Transaction>>> pendingGrouped = groupTransactions(transactionPool.getPendingTransactions()); 145  Map<RskAddress, Map<BigInteger, List<Transaction>>> queuedGrouped = groupTransactions(transactionPool.getQueuedTransactions()); 146  contentProps.put(PENDING, serializeTransactions(pendingGrouped, this::summarySerializer)); 147  contentProps.put(QUEUED, serializeTransactions(queuedGrouped, this::summarySerializer)); 148  JsonNode node = jsonNodeFactory.objectNode().setAll(contentProps); 149  return node; 150  } 151  152  /** 153  * This method should return 2 integers for pending and queued transactions 154  * These value represents 155  * the number of transactions currently pending for inclusion in the next block(s), 156  * as well as the ones that are being scheduled for future execution only. 157  * "{"pending": 0, "queued": 0}" 158  */ 159  @Override 160  public JsonNode status() { 161  Map<String, JsonNode> txProps = new HashMap<>(); 162  txProps.put(PENDING, jsonNodeFactory.numberNode(transactionPool.getPendingTransactions().size())); 163  txProps.put(QUEUED, jsonNodeFactory.numberNode(transactionPool.getQueuedTransactions().size())); 164  JsonNode node = jsonNodeFactory.objectNode().setAll(txProps); 165  return node; 166  } 167 }