diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java index e77f97f1591..328b8be17c1 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java @@ -21,6 +21,7 @@ import co.rsk.core.bc.AccountInformationProvider; import co.rsk.db.RepositoryLocator; import co.rsk.util.HexUtils; +import org.bouncycastle.util.encoders.DecoderException; import org.ethereum.core.Block; import org.ethereum.core.Blockchain; import org.ethereum.core.Transaction; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Optional; +import static co.rsk.crypto.Keccak256.HASH_LEN; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.blockNotFound; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; @@ -70,14 +72,22 @@ public Web3InformationRetriever(TransactionPool transactionPool, */ public Optional getBlock(String identifier) { Block block; - if (PENDING.equals(identifier)) { - block = executionBlockRetriever.retrieveExecutionBlock(identifier).getBlock(); - } else if (LATEST.equals(identifier)) { - block = blockchain.getBestBlock(); - } else if (EARLIEST.equals(identifier)) { - block = blockchain.getBlockByNumber(0); - } else { - block = this.blockchain.getBlockByNumber(getBlockNumber(identifier)); + + switch (identifier) { + case PENDING: + block = executionBlockRetriever.retrieveExecutionBlock(identifier).getBlock(); + break; + case LATEST: + block = blockchain.getBestBlock(); + break; + case EARLIEST: + block = blockchain.getBlockByNumber(0); + break; + default: + byte[] hash = getBlockHash(identifier); + block = hash.length == HASH_LEN ? + blockchain.getBlockByHash(hash) + : blockchain.getBlockByNumber(getBlockNumber(identifier)); } return Optional.ofNullable(block); @@ -134,4 +144,14 @@ private long getBlockNumber(String identifier) { } return blockNumber; } + + private byte[] getBlockHash(String identifier) { + byte[] blockHash; + try { + blockHash = HexUtils.stringHexToByteArray(identifier); + } catch (DecoderException e) { + throw invalidParamError(String.format("invalid blockhash %s", identifier)); + } + return blockHash; + } } diff --git a/rskj-core/src/test/java/co/rsk/rpc/Web3InformationRetrieverTest.java b/rskj-core/src/test/java/co/rsk/rpc/Web3InformationRetrieverTest.java index af1df1192ca..b4ac8badf70 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/Web3InformationRetrieverTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/Web3InformationRetrieverTest.java @@ -6,6 +6,7 @@ import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; import co.rsk.db.RepositorySnapshot; +import co.rsk.util.HexUtils; import org.ethereum.TestUtils; import org.ethereum.core.*; import org.ethereum.rpc.exception.RskJsonRpcRequestException; @@ -90,6 +91,18 @@ void getBlock_number() { assertEquals(secondBlock, result.get()); } + @Test + void getBlock_hash() { + String hash = "0x0000000000000000000000000000000000000000000000000000000000000002"; + byte[] bytesHash = HexUtils.stringHexToByteArray(hash); + Block secondBlock = mock(Block.class); + when(blockchain.getBlockByHash(bytesHash)).thenReturn(secondBlock); + Optional result = target.getBlock(hash); + + assertTrue(result.isPresent()); + assertEquals(secondBlock, result.get()); + } + @Test void getBlock_notFound() { Optional result = target.getBlock("0x2"); 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 1ac91726598..ecbf44231ec 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -383,6 +383,34 @@ void getStorageAtAccountAndBlockHash() { new BlockRefParam(blockRef))); } + @Test + //[ "0x
", "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" -> return storage at given address in genesis block + void getStorageAtAccountAndBlockHashWithSingleIdentifier() { + //given + final ChainParams chain = chainWithAccount10kBalance(false); + // when + String result = chain.web3.eth_getStorageAt( + new HexAddressParam(chain.accountAddress), + new HexNumberParam("0x0"), + new BlockRefParam( "0x" + chain.block.getPrintableHash())); + // then + assertEquals(NON_EXISTING_KEY_RESPONSE, result ); + } + + @Test + //[ "0x
", "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" -> return storage at given address in genesis block + void getStorageAtAccountAndBlockHashWithSingleIdentifier_whenItsAInvalidHash() { + //given + String invalidBlockIdentifier = "0x00011231234123"; + final ChainParams chain = chainWithAccount10kBalance(false); + //when-then + Exception ex = assertThrows(RskJsonRpcRequestException.class, () -> chain.web3.eth_getStorageAt( + new HexAddressParam(chain.accountAddress), + new HexNumberParam("0x0"), + new BlockRefParam( invalidBlockIdentifier))); + assertEquals("Block " + invalidBlockIdentifier + " not found", ex.getMessage()); + } + @Test //[ "0x
", { "blockHash": "0x" } -> raise block-not-found error void getStorageAtAccountAndNonExistentBlockHash() { @@ -810,7 +838,7 @@ void getTransactionReceiptNotInMainBlockchain() { txs.add(tx); Block genesis = world.getBlockChain().getBestBlock(); Block block1 = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).parent(genesis).difficulty(3l).transactions(txs).build(); + world.getBlockStore()).trieStore(world.getTrieStore()).parent(genesis).difficulty(3L).transactions(txs).build(); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block1)); Block block1b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), world.getBlockStore()).trieStore(world.getTrieStore()).parent(genesis) @@ -1018,7 +1046,7 @@ void getTransactionCount() { Transaction tx = new TransactionBuilder().sender(acc1).receiver(acc2).value(BigInteger.valueOf(1000000)).build(); List txs = new ArrayList<>(); txs.add(tx); - Block block1 = createCanonicalBlock(world, txs); + createCanonicalBlock(world, txs); String accountAddress = ByteUtil.toHexString(acc1.getAddress().getBytes()); @@ -3108,7 +3136,7 @@ void transactionReceiptAndResultHasTypeField() { Transaction tx = new TransactionBuilder().sender(acc1).receiver(acc2).value(BigInteger.valueOf(1000000)).build(); List txs = new ArrayList<>(); txs.add(tx); - Block block1 = createCanonicalBlock(world, txs); + createCanonicalBlock(world, txs); String hashString = tx.getHash().toHexString(); TxHashParam txHashParam = new TxHashParam(hashString);