Coverage Summary for Class: LogFilter (org.ethereum.rpc)

Class Method, % Line, %
LogFilter 0% (0/10) 0% (0/101)
LogFilter$LogFilterEvent 0% (0/2) 0% (0/3)
Total 0% (0/12) 0% (0/104)


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 org.ethereum.rpc; 20  21 import co.rsk.core.RskAddress; 22 import co.rsk.logfilter.BlocksBloom; 23 import co.rsk.logfilter.BlocksBloomStore; 24 import org.ethereum.core.*; 25 import org.ethereum.db.TransactionInfo; 26 import org.ethereum.vm.LogInfo; 27  28 import java.util.Collection; 29  30 import static org.ethereum.rpc.TypeConverter.stringHexToByteArray; 31  32 /** 33  * Created by ajlopez on 17/01/2018. 34  */ 35 public class LogFilter extends Filter { 36  class LogFilterEvent extends FilterEvent { 37  private final LogFilterElement el; 38  39  LogFilterEvent(LogFilterElement el) { 40  this.el = el; 41  } 42  43  @Override 44  public LogFilterElement getJsonEventObject() { 45  return el; 46  } 47  } 48  49  private AddressesTopicsFilter addressesTopicsFilter; 50  private boolean fromLatestBlock; 51  private boolean toLatestBlock; 52  private final Blockchain blockchain; 53  54  public LogFilter(AddressesTopicsFilter addressesTopicsFilter, Blockchain blockchain, boolean fromLatestBlock, boolean toLatestBlock) { 55  this.addressesTopicsFilter = addressesTopicsFilter; 56  this.blockchain = blockchain; 57  this.fromLatestBlock = fromLatestBlock; 58  this.toLatestBlock = toLatestBlock; 59  } 60  61  void onLogMatch(LogInfo logInfo, Block b, int txIndex, Transaction tx, int logIdx) { 62  add(new LogFilterEvent(new LogFilterElement(logInfo, b, txIndex, tx, logIdx))); 63  } 64  65  void onTransaction(Transaction tx, Block b, int txIndex) { 66  TransactionInfo txInfo = blockchain.getTransactionInfo(tx.getHash().getBytes()); 67  TransactionReceipt receipt = txInfo.getReceipt(); 68  69  LogFilterElement[] logs = new LogFilterElement[receipt.getLogInfoList().size()]; 70  71  for (int i = 0; i < logs.length; i++) { 72  LogInfo logInfo = receipt.getLogInfoList().get(i); 73  74  if (addressesTopicsFilter.matchesExactly(logInfo)) { 75  onLogMatch(logInfo, b, txIndex, receipt.getTransaction(), i); 76  } 77  } 78  } 79  80  void onBlock(Block b) { 81  if (addressesTopicsFilter.matchBloom(new Bloom(b.getLogBloom()))) { 82  int txIdx = 0; 83  84  for (Transaction tx : b.getTransactionsList()) { 85  onTransaction(tx, b, txIdx); 86  txIdx++; 87  } 88  } 89  } 90  91  @Override 92  public void newBlockReceived(Block b) { 93  if (this.fromLatestBlock) { 94  this.clearEvents(); 95  onBlock(b); 96  } 97  else if (this.toLatestBlock) { 98  onBlock(b); 99  } 100  } 101  102  @Override 103  public void newPendingTx(Transaction tx) { 104  //empty method 105  } 106  107  public static LogFilter fromFilterRequest(Web3.FilterRequest fr, Blockchain blockchain, BlocksBloomStore blocksBloomStore) throws Exception { 108  RskAddress[] addresses; 109  110  // Now, there is an array of array of topics 111  // first level are topic filters by position 112  // second level contains OR topic filters for that position 113  // null value matches anything 114  Topic[][] topics; 115  116  if (fr.address instanceof String) { 117  addresses = new RskAddress[] { new RskAddress(stringHexToByteArray((String) fr.address)) }; 118  } else if (fr.address instanceof Collection<?>) { 119  Collection<?> iterable = (Collection<?>)fr.address; 120  121  addresses = iterable.stream() 122  .filter(String.class::isInstance) 123  .map(String.class::cast) 124  .map(TypeConverter::stringHexToByteArray) 125  .map(RskAddress::new) 126  .toArray(RskAddress[]::new); 127  } 128  else { 129  addresses = new RskAddress[0]; 130  } 131  132  if (fr.topics != null) { 133  topics = new Topic[fr.topics.length][]; 134  135  for (int nt = 0; nt < fr.topics.length; nt++) { 136  Object topic = fr.topics[nt]; 137  138  if (topic == null) { 139  topics[nt] = new Topic[0]; 140  } else if (topic instanceof String) { 141  topics[nt] = new Topic[] { new Topic((String) topic) }; 142  } else if (topic instanceof Collection<?>) { 143  // TODO list of topics as topic with OR logic 144  145  Collection<?> iterable = (Collection<?>)topic; 146  147  topics[nt] = iterable.stream() 148  .filter(String.class::isInstance) 149  .map(String.class::cast) 150  .map(TypeConverter::stringHexToByteArray) 151  .map(Topic::new) 152  .toArray(Topic[]::new); 153  } 154  } 155  } 156  else { 157  topics = null; 158  } 159  160  AddressesTopicsFilter addressesTopicsFilter = new AddressesTopicsFilter(addresses, topics); 161  162  // TODO review pending transaction processing 163  // when fromBlock and/or toBlock are "pending" 164  165  // Default from block value 166  if (fr.fromBlock == null) { 167  fr.fromBlock = "latest"; 168  } 169  170  // Default to block value 171  if (fr.toBlock == null) { 172  fr.toBlock = "latest"; 173  } 174  175  boolean fromLatestBlock = "latest".equalsIgnoreCase(fr.fromBlock); 176  boolean toLatestBlock = "latest".equalsIgnoreCase(fr.toBlock); 177  178  LogFilter filter = new LogFilter(addressesTopicsFilter, blockchain, fromLatestBlock, toLatestBlock); 179  180  retrieveHistoricalData(fr, blockchain, filter, blocksBloomStore); 181  182  return filter; 183  } 184  185  private static void retrieveHistoricalData(Web3.FilterRequest fr, Blockchain blockchain, LogFilter filter, BlocksBloomStore blocksBloomStore) throws Exception { 186  Block blockFrom = isBlockWord(fr.fromBlock) ? null : Web3Impl.getBlockByNumberOrStr(fr.fromBlock, blockchain); 187  Block blockTo = isBlockWord(fr.toBlock) ? null : Web3Impl.getBlockByNumberOrStr(fr.toBlock, blockchain); 188  189  if (blockFrom == null && "earliest".equalsIgnoreCase(fr.fromBlock)) { 190  blockFrom = blockchain.getBlockByNumber(0); 191  } 192  193  if (blockFrom != null) { 194  // need to add historical data 195  blockTo = blockTo == null ? blockchain.getBestBlock() : blockTo; 196  197  processBlocks(blockFrom.getNumber(), blockTo.getNumber(), filter, blockchain, blocksBloomStore); 198  } 199  else if ("latest".equalsIgnoreCase(fr.fromBlock)) { 200  filter.onBlock(blockchain.getBestBlock()); 201  } 202  } 203  204  private static void processBlocks(long fromBlockNumber, long toBlockNumber, LogFilter filter, Blockchain blockchain, BlocksBloomStore blocksBloomStore) { 205  BlocksBloom auxiliaryBlocksBloom = null; 206  long bestBlockNumber = blockchain.getBestBlock().getNumber(); 207  208  for (long blockNum = fromBlockNumber; blockNum <= toBlockNumber; blockNum++) { 209  boolean isConfirmedBlock = blockNum <= bestBlockNumber - blocksBloomStore.getNoConfirmations(); 210  211  if (isConfirmedBlock) { 212  if (blocksBloomStore.firstNumberInRange(blockNum) == blockNum) { 213  if (blocksBloomStore.hasBlockNumber(blockNum)) { 214  BlocksBloom blocksBloom = blocksBloomStore.getBlocksBloomByNumber(blockNum); 215  216  if (!filter.addressesTopicsFilter.matchBloom(blocksBloom.getBloom())) { 217  blockNum = blocksBloomStore.lastNumberInRange(blockNum); 218  continue; 219  } 220  } 221  222  auxiliaryBlocksBloom = new BlocksBloom(); 223  } 224  225  Block block = blockchain.getBlockByNumber(blockNum); 226  227  if (auxiliaryBlocksBloom != null) { 228  auxiliaryBlocksBloom.addBlockBloom(blockNum, new Bloom(block.getLogBloom())); 229  } 230  231  if (auxiliaryBlocksBloom != null && blocksBloomStore.lastNumberInRange(blockNum) == blockNum) { 232  blocksBloomStore.addBlocksBloom(auxiliaryBlocksBloom); 233  } 234  235  filter.onBlock(block); 236  } 237  else { 238  filter.onBlock(blockchain.getBlockByNumber(blockNum)); 239  } 240  } 241  } 242  243  private static boolean isBlockWord(String id) { 244  return "latest".equalsIgnoreCase(id) || "pending".equalsIgnoreCase(id) || "earliest".equalsIgnoreCase(id); 245  } 246 }