diff --git a/rskj-core/src/main/java/co/rsk/core/RskFactory.java b/rskj-core/src/main/java/co/rsk/core/RskFactory.java index 2d38fe2457..39c96cd327 100644 --- a/rskj-core/src/main/java/co/rsk/core/RskFactory.java +++ b/rskj-core/src/main/java/co/rsk/core/RskFactory.java @@ -25,6 +25,7 @@ import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryImpl; import co.rsk.metrics.BlockHeaderElement; +import co.rsk.logfilter.BlocksBloomStore; import co.rsk.metrics.HashRateCalculator; import co.rsk.metrics.HashRateCalculatorMining; import co.rsk.metrics.HashRateCalculatorNonMining; @@ -205,7 +206,8 @@ public Web3 getWeb3(Rsk rsk, BlockProcessor nodeBlockProcessor, HashRateCalculator hashRateCalculator, ConfigCapabilities configCapabilities, - BuildInfo buildInfo) { + BuildInfo buildInfo, + BlocksBloomStore blocksBloomStore) { return new Web3RskImpl( rsk, blockchain, @@ -229,7 +231,8 @@ public Web3 getWeb3(Rsk rsk, nodeBlockProcessor, hashRateCalculator, configCapabilities, - buildInfo + buildInfo, + blocksBloomStore ); } @@ -284,6 +287,11 @@ public BlockChainImpl getBlockchain(BlockChainLoader blockChainLoader) { return blockChainLoader.loadBlockchain(); } + @Bean + public BlocksBloomStore getBlocksBloomStore() { + return new BlocksBloomStore(64, 20); + } + @Bean public TransactionPool getTransactionPool(org.ethereum.db.BlockStore blockStore, ReceiptStore receiptStore, diff --git a/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloom.java b/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloom.java new file mode 100644 index 0000000000..ce77b25107 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloom.java @@ -0,0 +1,64 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package co.rsk.logfilter; + +import org.ethereum.core.Bloom; + +/** + * Created by ajlopez on 29/01/2019. + */ +public class BlocksBloom { + private final Bloom bloom = new Bloom(); + private long fromBlock = -1; + private long toBlock = -1; + + public Bloom getBloom() { return this.bloom; } + + public long fromBlock() { return this.fromBlock; } + + public long toBlock() { return this.toBlock; } + + public long size() { + if (this.fromBlock == -1) { + return 0; + } + + return this.toBlock - this.fromBlock + 1; + } + + public void addBlockBloom(long blockNumber, Bloom blockBloom) { + if (fromBlock == -1) { + fromBlock = blockNumber; + toBlock = blockNumber; + } + else if (blockNumber == toBlock + 1) { + toBlock = blockNumber; + } + else { + throw new UnsupportedOperationException("Block out of sequence"); + } + + this.bloom.or(blockBloom); + } + + public boolean matches(Bloom bloom) { + return this.bloom.matches(bloom); + } +} diff --git a/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloomStore.java b/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloomStore.java new file mode 100644 index 0000000000..b15d7fa44d --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/logfilter/BlocksBloomStore.java @@ -0,0 +1,63 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package co.rsk.logfilter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by ajlopez on 05/02/2019. + */ +public class BlocksBloomStore { + private final int noBlocks; + private final int noConfirmations; + private final Map blocksBloom = new ConcurrentHashMap<>(); + + public BlocksBloomStore(int noBlocks, int noConfirmations) { + this.noBlocks = noBlocks; + this.noConfirmations = noConfirmations; + } + + public boolean hasBlockNumber(long blockNumber) { + return this.blocksBloom.containsKey(this.firstNumberInRange(blockNumber)); + } + + public BlocksBloom getBlocksBloomByNumber(long number) { + return this.blocksBloom.get(firstNumberInRange(number)); + } + + public void setBlocksBloom(BlocksBloom blocksBloom) { + this.blocksBloom.put(blocksBloom.fromBlock(), blocksBloom); + } + + public long firstNumberInRange(long number) { + return number - (number % this.noBlocks); + } + + public long lastNumberInRange(long number) { + return firstNumberInRange(number) + this.noBlocks - 1; + } + + public int getNoBlocks() { + return this.noBlocks; + } + + public int getNoConfirmations() { return this.noConfirmations; } +} diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3RskImpl.java b/rskj-core/src/main/java/co/rsk/rpc/Web3RskImpl.java index 1a014011d7..44655c80e0 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3RskImpl.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3RskImpl.java @@ -20,6 +20,7 @@ import co.rsk.config.RskSystemProperties; import co.rsk.core.NetworkStateExporter; +import co.rsk.logfilter.BlocksBloomStore; import co.rsk.metrics.HashRateCalculator; import co.rsk.mine.*; import co.rsk.net.BlockProcessor; @@ -83,11 +84,12 @@ public Web3RskImpl(Ethereum eth, BlockProcessor nodeBlockProcessor, HashRateCalculator hashRateCalculator, ConfigCapabilities configCapabilities, - BuildInfo buildInfo) { + BuildInfo buildInfo, + BlocksBloomStore blocksBloomStore) { super(eth, blockchain, transactionPool, blockStore, receiptStore, properties, minerClient, minerServer, personalModule, ethModule, evmModule, txPoolModule, mnrModule, debugModule, channelManager, repository, peerScoringManager, peerServer, nodeBlockProcessor, - hashRateCalculator, configCapabilities, buildInfo); + hashRateCalculator, configCapabilities, buildInfo, blocksBloomStore); this.networkStateExporter = networkStateExporter; this.blockStore = blockStore; diff --git a/rskj-core/src/main/java/co/rsk/vm/BitSet.java b/rskj-core/src/main/java/co/rsk/vm/BitSet.java index 9a0e9d136f..2b65110692 100644 --- a/rskj-core/src/main/java/co/rsk/vm/BitSet.java +++ b/rskj-core/src/main/java/co/rsk/vm/BitSet.java @@ -60,4 +60,8 @@ public boolean get(int position) { public int size() { return this.size; } + + protected byte[] getBytes() { + return this.bytes; + } } diff --git a/rskj-core/src/main/java/org/ethereum/core/Bloom.java b/rskj-core/src/main/java/org/ethereum/core/Bloom.java index 7de0a51609..640a850ba0 100644 --- a/rskj-core/src/main/java/org/ethereum/core/Bloom.java +++ b/rskj-core/src/main/java/org/ethereum/core/Bloom.java @@ -32,13 +32,13 @@ */ public class Bloom { + public static final int BLOOM_BYTES = 256; static final int _8STEPS = 8; static final int _3LOW_BITS = 7; static final int ENSURE_BYTE = 255; - byte[] data = new byte[256]; - + private byte[] data = new byte[BLOOM_BYTES]; public Bloom() { } diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java b/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java index 31e0fbe7cf..d8409018db 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java @@ -142,7 +142,7 @@ public byte[] getEncoded() { byte[] postTxStateRLP = RLP.encodeElement(this.postTxState); byte[] cumulativeGasRLP = RLP.encodeElement(this.cumulativeGas); byte[] gasUsedRLP = RLP.encodeElement(this.gasUsed); - byte[] bloomRLP = RLP.encodeElement(this.bloomFilter.data); + byte[] bloomRLP = RLP.encodeElement(this.bloomFilter.getData()); byte[] statusRLP = RLP.encodeElement(this.status); final byte[] logInfoListRLP; diff --git a/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java b/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java index 88195b8c36..d58bf0788b 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java @@ -19,6 +19,8 @@ package org.ethereum.rpc; import co.rsk.core.RskAddress; +import co.rsk.logfilter.BlocksBloom; +import co.rsk.logfilter.BlocksBloomStore; import org.ethereum.core.*; import org.ethereum.db.TransactionInfo; import org.ethereum.vm.LogInfo; @@ -102,7 +104,7 @@ public void newPendingTx(Transaction tx) { //empty method } - public static LogFilter fromFilterRequest(Web3.FilterRequest fr, Blockchain blockchain) throws Exception { + public static LogFilter fromFilterRequest(Web3.FilterRequest fr, Blockchain blockchain, BlocksBloomStore blocksBloomStore) throws Exception { RskAddress[] addresses; // Now, there is an array of array of topics @@ -175,12 +177,12 @@ public static LogFilter fromFilterRequest(Web3.FilterRequest fr, Blockchain bloc LogFilter filter = new LogFilter(addressesTopicsFilter, blockchain, fromLatestBlock, toLatestBlock); - retrieveHistoricalData(fr, blockchain, filter); + retrieveHistoricalData(fr, blockchain, filter, blocksBloomStore); return filter; } - private static void retrieveHistoricalData(Web3.FilterRequest fr, Blockchain blockchain, LogFilter filter) throws Exception { + private static void retrieveHistoricalData(Web3.FilterRequest fr, Blockchain blockchain, LogFilter filter, BlocksBloomStore blocksBloomStore) throws Exception { Block blockFrom = isBlockWord(fr.fromBlock) ? null : Web3Impl.getBlockByNumberOrStr(fr.fromBlock, blockchain); Block blockTo = isBlockWord(fr.toBlock) ? null : Web3Impl.getBlockByNumberOrStr(fr.toBlock, blockchain); @@ -192,15 +194,52 @@ private static void retrieveHistoricalData(Web3.FilterRequest fr, Blockchain blo // need to add historical data blockTo = blockTo == null ? blockchain.getBestBlock() : blockTo; - for (long blockNum = blockFrom.getNumber(); blockNum <= blockTo.getNumber(); blockNum++) { - filter.onBlock(blockchain.getBlockByNumber(blockNum)); - } + processBlocks(blockFrom.getNumber(), blockTo.getNumber(), filter, blockchain, blocksBloomStore); } else if ("latest".equalsIgnoreCase(fr.fromBlock)) { filter.onBlock(blockchain.getBestBlock()); } } + private static void processBlocks(long fromBlockNumber, long toBlockNumber, LogFilter filter, Blockchain blockchain, BlocksBloomStore blocksBloomStore) { + BlocksBloom auxiliaryBlocksBloom = null; + long bestBlockNumber = blockchain.getBestBlock().getNumber(); + + for (long blockNum = fromBlockNumber; blockNum <= toBlockNumber; blockNum++) { + boolean isConfirmedBlock = blockNum <= bestBlockNumber - blocksBloomStore.getNoConfirmations(); + + if (isConfirmedBlock) { + if (blocksBloomStore.firstNumberInRange(blockNum) == blockNum) { + if (blocksBloomStore.hasBlockNumber(blockNum)) { + BlocksBloom blocksBloom = blocksBloomStore.getBlocksBloomByNumber(blockNum); + + if (!filter.addressesTopicsFilter.matchBloom(blocksBloom.getBloom())) { + blockNum = blocksBloomStore.lastNumberInRange(blockNum); + continue; + } + } + + auxiliaryBlocksBloom = new BlocksBloom(); + } + + Block block = blockchain.getBlockByNumber(blockNum); + + if (auxiliaryBlocksBloom != null) { + auxiliaryBlocksBloom.addBlockBloom(blockNum, new Bloom(block.getLogBloom())); + } + + if (auxiliaryBlocksBloom != null && blocksBloomStore.lastNumberInRange(blockNum) == blockNum) { + blocksBloomStore.setBlocksBloom(auxiliaryBlocksBloom); + } + + filter.onBlock(block); + } + else { + filter.onBlock(blockchain.getBlockByNumber(blockNum)); + } + } + } + private static boolean isBlockWord(String id) { return "latest".equalsIgnoreCase(id) || "pending".equalsIgnoreCase(id) || "earliest".equalsIgnoreCase(id); } diff --git a/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java b/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java index ce2701c839..d817ecb8a3 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/Web3Impl.java @@ -23,6 +23,7 @@ import co.rsk.core.RskAddress; import co.rsk.core.bc.AccountInformationProvider; import co.rsk.crypto.Keccak256; +import co.rsk.logfilter.BlocksBloomStore; import co.rsk.metrics.HashRateCalculator; import co.rsk.mine.MinerClient; import co.rsk.mine.MinerServer; @@ -97,6 +98,8 @@ public class Web3Impl implements Web3 { private final FilterManager filterManager; private final BuildInfo buildInfo; + private final BlocksBloomStore blocksBloomStore; + private final PersonalModule personalModule; private final EthModule ethModule; private final EvmModule evmModule; @@ -126,7 +129,8 @@ protected Web3Impl( BlockProcessor nodeBlockProcessor, HashRateCalculator hashRateCalculator, ConfigCapabilities configCapabilities, - BuildInfo buildInfo) { + BuildInfo buildInfo, + BlocksBloomStore blocksBloomStore) { this.eth = eth; this.blockchain = blockchain; this.blockStore = blockStore; @@ -150,6 +154,7 @@ protected Web3Impl( this.config = config; filterManager = new FilterManager(eth); this.buildInfo = buildInfo; + this.blocksBloomStore = blocksBloomStore; initialBlockNumber = this.blockchain.getBestBlock().getNumber(); personalModule.init(this.config); @@ -841,7 +846,7 @@ public String eth_newFilter(FilterRequest fr) throws Exception { String str = null; try { - Filter filter = LogFilter.fromFilterRequest(fr, blockchain); + Filter filter = LogFilter.fromFilterRequest(fr, blockchain, blocksBloomStore); int id = filterManager.registerFilter(filter); return str = toJsonHex(id); diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index 45ac5a01b1..637ce418b8 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -312,4 +312,4 @@ cache { # precaution we'll set the limit to 100K entries. max-elements: 100000 } -} \ No newline at end of file +} diff --git a/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomStoreTest.java b/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomStoreTest.java new file mode 100644 index 0000000000..e9c10058bc --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomStoreTest.java @@ -0,0 +1,61 @@ +package co.rsk.logfilter; + +import org.ethereum.core.Bloom; +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by ajlopez on 05/02/2019. + */ +public class BlocksBloomStoreTest { + @Test + public void getFirstNumberInRange() { + BlocksBloomStore blocksBloomStore = new BlocksBloomStore(64, 0); + + Assert.assertEquals(0, blocksBloomStore.firstNumberInRange(0)); + Assert.assertEquals(0, blocksBloomStore.firstNumberInRange(1)); + Assert.assertEquals(blocksBloomStore.getNoBlocks(), blocksBloomStore.firstNumberInRange(blocksBloomStore.getNoBlocks())); + Assert.assertEquals(blocksBloomStore.getNoBlocks(), blocksBloomStore.firstNumberInRange(blocksBloomStore.getNoBlocks() * 2 - 1)); + } + + @Test + public void getLastNumberInRange() { + BlocksBloomStore blocksBloomStore = new BlocksBloomStore(64, 0); + + Assert.assertEquals(blocksBloomStore.getNoBlocks() - 1, blocksBloomStore.lastNumberInRange(0)); + Assert.assertEquals(blocksBloomStore.getNoBlocks() - 1, blocksBloomStore.lastNumberInRange(1)); + Assert.assertEquals(blocksBloomStore.getNoBlocks() * 2 - 1, blocksBloomStore.lastNumberInRange(blocksBloomStore.getNoBlocks())); + Assert.assertEquals(blocksBloomStore.getNoBlocks() * 2 - 1, blocksBloomStore.lastNumberInRange(blocksBloomStore.getNoBlocks() * 2 - 1)); + } + + @Test + public void noBlocksBloom() { + BlocksBloomStore blocksBloomStore = new BlocksBloomStore(64, 0); + + Assert.assertNull(blocksBloomStore.getBlocksBloomByNumber(0)); + Assert.assertNull(blocksBloomStore.getBlocksBloomByNumber(1)); + Assert.assertNull(blocksBloomStore.getBlocksBloomByNumber(blocksBloomStore.getNoBlocks() - 1)); + Assert.assertNull(blocksBloomStore.getBlocksBloomByNumber(blocksBloomStore.getNoBlocks())); + } + + @Test + public void setBlocksBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + byte[] bytes1 = new byte[Bloom.BLOOM_BYTES]; + bytes1[0] = 0x01; + byte[] bytes2 = new byte[Bloom.BLOOM_BYTES]; + bytes2[1] = 0x10; + + Bloom bloom1 = new Bloom(bytes1); + Bloom bloom2 = new Bloom(bytes2); + + BlocksBloomStore blocksBloomStore = new BlocksBloomStore(64, 0); + + blocksBloom.addBlockBloom(blocksBloomStore.getNoBlocks(), bloom1); + blocksBloom.addBlockBloom(blocksBloomStore.getNoBlocks() + 1, bloom2); + + blocksBloomStore.setBlocksBloom(blocksBloom); + + Assert.assertSame(blocksBloom, blocksBloomStore.getBlocksBloomByNumber(blocksBloomStore.getNoBlocks())); + } +} diff --git a/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomTest.java b/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomTest.java new file mode 100644 index 0000000000..0fd35ed3d4 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/logfilter/BlocksBloomTest.java @@ -0,0 +1,126 @@ +package co.rsk.logfilter; + +import org.ethereum.core.Bloom; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +/** + * Created by ajlopez on 20/01/2019. + */ +public class BlocksBloomTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void createBlocksBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + + Assert.assertEquals(0, blocksBloom.size()); + + byte[] bytes = new byte[Bloom.BLOOM_BYTES]; + + Assert.assertArrayEquals(bytes, blocksBloom.getBloom().getData()); + } + + @Test + public void addBlockToBlocksBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + byte[] bytes = new byte[Bloom.BLOOM_BYTES]; + bytes[0] = 0x01; + Bloom bloom = new Bloom(bytes); + + blocksBloom.addBlockBloom(1, bloom); + + Assert.assertEquals(1, blocksBloom.size()); + Assert.assertEquals(1, blocksBloom.fromBlock()); + Assert.assertEquals(1, blocksBloom.toBlock()); + + Assert.assertArrayEquals(bytes, blocksBloom.getBloom().getData()); + } + + @Test + public void addTwoBlocksToBlocksBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + byte[] bytes1 = new byte[Bloom.BLOOM_BYTES]; + bytes1[0] = 0x01; + byte[] bytes2 = new byte[Bloom.BLOOM_BYTES]; + bytes2[1] = 0x10; + + Bloom bloom1 = new Bloom(bytes1); + Bloom bloom2 = new Bloom(bytes2); + + blocksBloom.addBlockBloom(1, bloom1); + blocksBloom.addBlockBloom(2, bloom2); + + Assert.assertEquals(2, blocksBloom.size()); + Assert.assertEquals(1, blocksBloom.fromBlock()); + Assert.assertEquals(2, blocksBloom.toBlock()); + + bloom1.or(bloom2); + + Assert.assertArrayEquals(bloom1.getData(), blocksBloom.getBloom().getData()); + } + + @Test + public void addTwoNonConsecutiveBlocksToBlocksBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + byte[] bytes1 = new byte[Bloom.BLOOM_BYTES]; + bytes1[0] = 0x01; + byte[] bytes2 = new byte[Bloom.BLOOM_BYTES]; + bytes2[1] = 0x10; + + Bloom bloom1 = new Bloom(bytes1); + Bloom bloom2 = new Bloom(bytes2); + + exception.expect(UnsupportedOperationException.class); + exception.expectMessage("Block out of sequence"); + + blocksBloom.addBlockBloom(1, bloom1); + blocksBloom.addBlockBloom(3, bloom2); + } + + @Test + public void matchesBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + + byte[] bytes1 = new byte[Bloom.BLOOM_BYTES]; + bytes1[0] = 0x01; + byte[] bytes2 = new byte[Bloom.BLOOM_BYTES]; + bytes2[1] = 0x10; + + Bloom bloom1 = new Bloom(bytes1); + Bloom bloom2 = new Bloom(bytes2); + + blocksBloom.addBlockBloom(1, bloom1); + blocksBloom.addBlockBloom(2, bloom2); + + Assert.assertTrue(blocksBloom.matches(bloom1)); + Assert.assertTrue(blocksBloom.matches(bloom2)); + + bloom1.or(bloom2); + + Assert.assertTrue(blocksBloom.matches(bloom1)); + } + + @Test + public void doesNotMatchBloom() { + BlocksBloom blocksBloom = new BlocksBloom(); + + byte[] bytes1 = new byte[Bloom.BLOOM_BYTES]; + bytes1[0] = 0x01; + byte[] bytes2 = new byte[Bloom.BLOOM_BYTES]; + bytes2[1] = 0x10; + + Bloom bloom1 = new Bloom(bytes1); + Bloom bloom2 = new Bloom(bytes2); + + Assert.assertFalse(blocksBloom.matches(bloom1)); + Assert.assertFalse(blocksBloom.matches(bloom2)); + + bloom1.or(bloom2); + + Assert.assertFalse(blocksBloom.matches(bloom1)); + } +} diff --git a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java index 9a88bbebb1..62b918865a 100644 --- a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java @@ -321,6 +321,7 @@ private Web3Impl createEnvironment(BlockChainImpl blockchain, ReceiptStore recei null, null, configCapabilities, + null, null ); } diff --git a/rskj-core/src/test/java/co/rsk/rpc/Web3ImplRpcTest.java b/rskj-core/src/test/java/co/rsk/rpc/Web3ImplRpcTest.java index 252a6cc949..c08ed3f899 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/Web3ImplRpcTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/Web3ImplRpcTest.java @@ -48,7 +48,7 @@ public void getRpcModules() { null, null, null, null, null, null, repository, null, null, null, null, null, - null, null, null, null); + null, null, null, null, null); Map result = web3.rpc_modules(); diff --git a/rskj-core/src/test/java/co/rsk/rpc/Web3RskImplTest.java b/rskj-core/src/test/java/co/rsk/rpc/Web3RskImplTest.java index 7464a67ff2..c842cfdedc 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/Web3RskImplTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/Web3RskImplTest.java @@ -24,6 +24,7 @@ import co.rsk.core.Wallet; import co.rsk.core.WalletFactory; import co.rsk.crypto.Keccak256; +import co.rsk.logfilter.BlocksBloomStore; import co.rsk.peg.PegTestUtils; import co.rsk.rpc.modules.debug.DebugModule; import co.rsk.rpc.modules.debug.DebugModuleImpl; @@ -100,6 +101,7 @@ public void web3_ext_dumpState() throws Exception { null, null, null, + null, null ); web3.ext_dumpState(); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index 09bac16476..bbe2985b74 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -23,6 +23,7 @@ import co.rsk.core.RskImpl; import co.rsk.core.Wallet; import co.rsk.core.WalletFactory; +import co.rsk.logfilter.BlocksBloomStore; import co.rsk.rpc.ExecutionBlockRetriever; import co.rsk.rpc.Web3RskImpl; import co.rsk.rpc.modules.debug.DebugModule; @@ -272,6 +273,19 @@ public void getLogsFromBlockchainWithThreeEmptyBlocks() throws Exception { Assert.assertEquals(0, logs.length); } + @Test + public void getLogsTwiceFromBlockchainWithThreeEmptyBlocks() throws Exception { + addTwoEmptyBlocks(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(0, logs.length); + } + @Test public void getLogsFromBlockchainWithContractCreation() throws Exception { addContractCreationWithoutEvents(); @@ -284,6 +298,19 @@ public void getLogsFromBlockchainWithContractCreation() throws Exception { Assert.assertEquals(0, logs.length); } + @Test + public void getLogsTwiceFromBlockchainWithContractCreation() throws Exception { + addContractCreationWithoutEvents(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(0, logs.length); + } + @Test public void getLogsFromBlockchainWithEventInContractCreation() throws Exception { addEventInContractCreation(); @@ -301,6 +328,24 @@ public void getLogsFromBlockchainWithEventInContractCreation() throws Exception Assert.assertEquals(txdto.contractAddress,((LogFilterElement)logs[0]).address); } + @Test + public void getLogsTwiceFromBlockchainWithEventInContractCreation() throws Exception { + addEventInContractCreation(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(1, logs.length); + + String txhash = ((LogFilterElement)logs[0]).transactionHash; + TransactionReceiptDTO txdto = web3.eth_getTransactionReceipt(txhash); + + Assert.assertEquals(txdto.contractAddress,((LogFilterElement)logs[0]).address); + } + @Test public void getLogsFromBlockchainWithInvokeContract() throws Exception { addContractInvoke(); @@ -319,6 +364,26 @@ public void getLogsFromBlockchainWithInvokeContract() throws Exception { Assert.assertEquals(txdto.contractAddress,((LogFilterElement)logs[1]).address); } + + @Test + public void getLogsTwiceFromBlockchainWithInvokeContract() throws Exception { + addContractInvoke(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(2, logs.length); + + String txhash = ((LogFilterElement)logs[0]).transactionHash; + TransactionReceiptDTO txdto = web3.eth_getTransactionReceipt(txhash); + + Assert.assertEquals(txdto.contractAddress,((LogFilterElement)logs[0]).address); + Assert.assertEquals(txdto.contractAddress,((LogFilterElement)logs[1]).address); + } + @Test public void getLogsFromBlockchainWithCallContract() throws Exception { addContractCall(); @@ -331,6 +396,19 @@ public void getLogsFromBlockchainWithCallContract() throws Exception { Assert.assertEquals(3, logs.length); } + @Test + public void getLogsTwiceFromBlockchainWithCallContract() throws Exception { + addContractCall(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(3, logs.length); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByContractAddress() throws Exception { addContractCall(); @@ -350,6 +428,26 @@ public void getLogsFromBlockchainWithCallContractAndFilterByContractAddress() th Assert.assertEquals(address,((LogFilterElement)logs[2]).address); } + @Test + public void getLogsTwoceFromBlockchainWithCallContractAndFilterByContractAddress() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.address = Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(3, logs.length); + + String address = "0x" + fr.address; + + Assert.assertEquals(address,((LogFilterElement)logs[0]).address); + Assert.assertEquals(address,((LogFilterElement)logs[1]).address); + Assert.assertEquals(address,((LogFilterElement)logs[2]).address); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByUnknownContractAddress() throws Exception { addContractCall(); @@ -365,6 +463,22 @@ public void getLogsFromBlockchainWithCallContractAndFilterByUnknownContractAddre Assert.assertEquals(0, logs.length); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByUnknownContractAddress() throws Exception { + addContractCall(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + List addresses = new ArrayList<>(); + addresses.add(Hex.toHexString(new byte[20])); + fr.address = addresses; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(0, logs.length); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByUnknownTopic() throws Exception { addContractCall(); @@ -379,6 +493,21 @@ public void getLogsFromBlockchainWithCallContractAndFilterByUnknownTopic() throw Assert.assertEquals(0, logs.length); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByUnknownTopic() throws Exception { + addContractCall(); + + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[1]; + fr.topics[0] = "0102030405060102030405060102030405060102030405060102030405060102"; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + Assert.assertEquals(0, logs.length); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopic() throws Exception { addContractCall(); @@ -396,6 +525,24 @@ public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopic() throws Assert.assertEquals(address,((LogFilterElement)logs[0]).address); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByKnownTopic() throws Exception { + addContractCall(); + + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[1]; + fr.topics[0] = GET_VALUED_EVENT_SIGNATURE; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(1, logs.length); + Assert.assertEquals(address,((LogFilterElement)logs[0]).address); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopicInList() throws Exception { addContractCall(); @@ -414,6 +561,25 @@ public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopicInList() t Assert.assertEquals(address,((LogFilterElement)logs[0]).address); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByKnownTopicInList() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[1]; + List topics = new ArrayList<>(); + topics.add(GET_VALUED_EVENT_SIGNATURE); + fr.topics[0] = topics; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(1, logs.length); + Assert.assertEquals(address,((LogFilterElement)logs[0]).address); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByKnownsTopicInList() throws Exception { addContractCall(); @@ -436,6 +602,29 @@ public void getLogsFromBlockchainWithCallContractAndFilterByKnownsTopicInList() } } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByKnownsTopicInList() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[1]; + List topics = new ArrayList<>(); + topics.add(GET_VALUED_EVENT_SIGNATURE); + topics.add(INC_EVENT_SIGNATURE); + fr.topics[0] = topics; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(2, logs.length); + + for (int k = 0; k < logs.length; k++) { + Assert.assertEquals(address, ((LogFilterElement) logs[k]).address); + } + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopicInListWithNull() throws Exception { addContractCall(); @@ -453,6 +642,24 @@ public void getLogsFromBlockchainWithCallContractAndFilterByKnownTopicInListWith Assert.assertEquals(address,((LogFilterElement)logs[0]).address); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterByKnownTopicInListWithNull() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[2]; + fr.topics[0] = GET_VALUED_EVENT_SIGNATURE; + fr.topics[1] = null; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(1, logs.length); + Assert.assertEquals(address,((LogFilterElement)logs[0]).address); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterWithNullTopic() throws Exception { addContractCall(); @@ -472,6 +679,26 @@ public void getLogsFromBlockchainWithCallContractAndFilterWithNullTopic() throws } } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterWithNullTopic() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[1]; + fr.topics[0] = null; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(3, logs.length); + + for (int k = 0; k < logs.length; k++) { + Assert.assertEquals(address, ((LogFilterElement) logs[k]).address); + } + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterWithTwoTopics() throws Exception { addContractCall(); @@ -492,6 +719,27 @@ public void getLogsFromBlockchainWithCallContractAndFilterWithTwoTopics() throws Assert.assertEquals("0x" + ONE_TOPIC, ((LogFilterElement) logs[0]).topics[1]); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterWithTwoTopics() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[2]; + fr.topics[0] = INC_EVENT_SIGNATURE; + fr.topics[1] = ONE_TOPIC; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(1, logs.length); + + Assert.assertEquals(address, ((LogFilterElement) logs[0]).address); + Assert.assertEquals("0x" + INC_EVENT_SIGNATURE, ((LogFilterElement) logs[0]).topics[0]); + Assert.assertEquals("0x" + ONE_TOPIC, ((LogFilterElement) logs[0]).topics[1]); + } + @Test public void getLogsFromBlockchainWithCallContractAndFilterBySecondTopic() throws Exception { addContractCall(); @@ -512,6 +760,27 @@ public void getLogsFromBlockchainWithCallContractAndFilterBySecondTopic() throws Assert.assertEquals("0x" + ONE_TOPIC, ((LogFilterElement) logs[0]).topics[1]); } + @Test + public void getLogsTwiceFromBlockchainWithCallContractAndFilterBySecondTopic() throws Exception { + addContractCall(); + Block block1 = blockChain.getBlockByNumber(1l); + Web3.FilterRequest fr = new Web3.FilterRequest(); + fr.fromBlock = "earliest"; + fr.topics = new Object[2]; + fr.topics[0] = null; + fr.topics[1] = ONE_TOPIC; + web3.eth_getLogs(fr); + Object[] logs = web3.eth_getLogs(fr); + + Assert.assertNotNull(logs); + String address = "0x" + Hex.toHexString(block1.getTransactionsList().get(0).getContractAddress().getBytes()); + Assert.assertEquals(1, logs.length); + + Assert.assertEquals(address, ((LogFilterElement) logs[0]).address); + Assert.assertEquals("0x" + INC_EVENT_SIGNATURE, ((LogFilterElement) logs[0]).topics[0]); + Assert.assertEquals("0x" + ONE_TOPIC, ((LogFilterElement) logs[0]).topics[1]); + } + @Test public void createMainContractWithoutEvents() throws Exception { Account acc1 = new AccountBuilder(blockChain).name("notDefault").balance(Coin.valueOf(10000000)).build(); @@ -698,7 +967,8 @@ private Web3Impl createWeb3() { null, null, new SimpleConfigCapabilities(), - null + null, + new BlocksBloomStore(2, 0) ); } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java index cbbbb0e98d..81165ad990 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplScoringTest.java @@ -382,6 +382,7 @@ private static Web3Impl createWeb3(PeerScoringManager peerScoringManager) { null, null, null, + null, null ); } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplSnapshotTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplSnapshotTest.java index 7bff86bb62..4cf3ad12c1 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplSnapshotTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplSnapshotTest.java @@ -181,6 +181,7 @@ private Web3Impl createWeb3(SimpleEthereum ethereum) { null, null, null, + null, null ); } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java index fe08538229..06c0aca58e 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -276,6 +276,7 @@ public void eth_mining() { null, null, null, + null, null ); @@ -956,6 +957,7 @@ public void eth_coinbase() { null, null, null, + null, null ); @@ -1275,6 +1277,7 @@ private Web3Impl createWeb3(SimpleEthereum eth, PeerServer peerServer) { null, null, null, + null, null ); } @@ -1330,7 +1333,8 @@ private Web3Impl createWeb3(Ethereum eth, Blockchain blockchain, TransactionPool nodeBlockProcessor, null, configCapabilities, - new BuildInfo("test", "test") + new BuildInfo("test", "test"), + null ); } @@ -1371,6 +1375,7 @@ public void eth_compileSolidity() throws Exception { null, null, null, + null, null ); String contract = "pragma solidity ^0.4.1; contract rsk { function multiply(uint a) returns(uint d) { return a * 7; } }"; @@ -1426,6 +1431,7 @@ public void eth_compileSolidityWithoutSolidity() throws Exception { null, null, null, + null, null );