Skip to content

Commit

Permalink
Add standard trace bad block to file JSON RPC method (#1403)
Browse files Browse the repository at this point in the history
Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
  • Loading branch information
matkt committed Sep 29, 2020
1 parent b5e8f4f commit cfaccb6
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Additions and Improvements
* Added support for the upcoming YOLOv2 ephemeral testnet and removed the flag for the deprecated YOLOv1 ephemeral testnet. [#1386](https://github.com/hyperledger/besu/pull/1386)
* Added `debug_standardTraceBlockToFile` JSON-RPC API. This API accepts a block hash and will replay the block. It returns a list of files containing the result of the trace (one file per transaction). [\#1392](https://github.com/hyperledger/besu/pull/1392)
* Added `debug_standardTraceBadBlockToFile` JSON-RPC API. This API is similar to `debug_standardTraceBlockToFile`, but can be used to obtain info about a block which has been rejected as invalid. [\#1403](https://github.com/hyperledger/besu/pull/1403)
* Added support for EIP-2929 to YOLOv2. [#1387](https://github.com/hyperledger/besu/pull/1387)
* Added `--start-block` and `--end-block` to the `blocks import` subcommand [\#1399](https://github.com/hyperledger/besu/pull/1399)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public enum RpcMethod {
DEBUG_TRACE_BLOCK_BY_HASH("debug_traceBlockByHash"),
DEBUG_TRACE_BLOCK_BY_NUMBER("debug_traceBlockByNumber"),
DEBUG_STANDARD_TRACE_BLOCK_TO_FILE("debug_standardTraceBlockToFile"),
DEBUG_STANDARD_TRACE_BAD_BLOCK_TO_FILE("debug_standardTraceBadBlockToFile"),
DEBUG_TRACE_TRANSACTION("debug_traceTransaction"),
DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"),
DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;

import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Supplier;

public class DebugStandardTraceBadBlockToFile extends DebugStandardTraceBlockToFile
implements JsonRpcMethod {

private final ProtocolSchedule protocolSchedule;

public DebugStandardTraceBadBlockToFile(
final Supplier<TransactionTracer> transactionTracerSupplier,
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
final Path dataDir) {
super(transactionTracerSupplier, blockchainQueries, dataDir);
this.protocolSchedule = protocolSchedule;
}

@Override
public String getName() {
return RpcMethod.DEBUG_STANDARD_TRACE_BAD_BLOCK_TO_FILE.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final Hash blockHash = requestContext.getRequiredParameter(0, Hash.class);
final Optional<TransactionTraceParams> transactionTraceParams =
requestContext.getOptionalParameter(1, TransactionTraceParams.class);

final Blockchain blockchain = blockchainQueries.get().getBlockchain();
final ProtocolSpec protocolSpec =
protocolSchedule.getByBlockNumber(blockchain.getChainHeadHeader().getNumber());
final BadBlockManager badBlockManager = protocolSpec.getBadBlocksManager();

return badBlockManager
.getBadBlock(blockHash)
.map(
block ->
(JsonRpcResponse)
new JsonRpcSuccessResponse(
requestContext.getRequest().getId(),
traceBlock(block, transactionTraceParams)))
.orElse(
new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.BLOCK_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@

public class DebugStandardTraceBlockToFile implements JsonRpcMethod {

protected final Supplier<BlockchainQueries> blockchainQueries;
private final Supplier<TransactionTracer> transactionTracerSupplier;
private final Supplier<BlockchainQueries> blockchainQueries;
private final Path dataDir;

public DebugStandardTraceBlockToFile(
Expand Down Expand Up @@ -77,7 +77,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
requestContext.getRequest().getId(), JsonRpcError.BLOCK_NOT_FOUND));
}

private List<String> traceBlock(
protected List<String> traceBlock(
final Block block, final Optional<TransactionTraceParams> transactionTraceParams) {
return transactionTracerSupplier
.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,12 @@ public <T> Optional<T> afterTransactionInBlock(

private <T> Optional<T> performActionWithBlock(
final Hash blockHash, final BlockAction<T> action) {
return getBlock(blockHash)
.flatMap(block -> performActionWithBlock(block.getHeader(), block.getBody(), action));
Optional<Block> maybeBlock = getBlock(blockHash);
if (maybeBlock.isEmpty()) {
maybeBlock = getBadBlock(blockHash);
}
return maybeBlock.flatMap(
block -> performActionWithBlock(block.getHeader(), block.getBody(), action));
}

private <T> Optional<T> performActionWithBlock(
Expand Down Expand Up @@ -161,6 +165,12 @@ private Optional<Block> getBlock(final Hash blockHash) {
return Optional.empty();
}

private Optional<Block> getBadBlock(final Hash blockHash) {
final ProtocolSpec protocolSpec =
protocolSchedule.getByBlockNumber(blockchain.getChainHeadHeader().getNumber());
return protocolSpec.getBadBlocksManager().getBadBlock(blockHash);
}

@FunctionalInterface
private interface BlockAction<T> {
Optional<T> perform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugBatchSendRawTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetBadBlocks;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugMetrics;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStandardTraceBadBlockToFile;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStandardTraceBlockToFile;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStorageRangeAt;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugTraceBlock;
Expand Down Expand Up @@ -90,6 +91,11 @@ protected Map<String, JsonRpcMethod> create() {
new DebugBatchSendRawTransaction(transactionPool),
new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult),
new DebugStandardTraceBlockToFile(
() -> new TransactionTracer(blockReplay), blockchainQueries, dataDir));
() -> new TransactionTracer(blockReplay), blockchainQueries, dataDir),
new DebugStandardTraceBadBlockToFile(
() -> new TransactionTracer(blockReplay),
blockchainQueries,
protocolSchedule,
dataDir));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class DebugStandardTraceBadBlockToFileTest {

@ClassRule public static final TemporaryFolder folder = new TemporaryFolder();

private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private final Blockchain blockchain = mock(Blockchain.class);
private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class);
private final TransactionTracer transactionTracer = mock(TransactionTracer.class);
private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class);

private final DebugStandardTraceBadBlockToFile debugStandardTraceBadBlockToFile =
new DebugStandardTraceBadBlockToFile(
() -> transactionTracer, blockchainQueries, protocolSchedule, folder.getRoot().toPath());

@Test
public void nameShouldBeDebugTraceTransaction() {
assertThat(debugStandardTraceBadBlockToFile.getName())
.isEqualTo("debug_standardTraceBadBlockToFile");
}

@SuppressWarnings("rawtypes")
@Test
public void shouldTraceTheTransactionUsingTheTransactionTracer() {

final BlockDataGenerator blockGenerator = new BlockDataGenerator();
final Block genesis = blockGenerator.genesisBlock();
final Block block =
blockGenerator.block(
new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash()));

final Object[] params = new Object[] {block.getHash(), new HashMap<>()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "debug_standardTraceBadBlockToFile", params));

final List<String> paths = new ArrayList<>();
paths.add("path-1");

final BadBlockManager badBlockManager = new BadBlockManager();
badBlockManager.addBadBlock(block);

final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
when(protocolSpec.getBadBlocksManager()).thenReturn(badBlockManager);
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getChainHeadHeader()).thenReturn(new BlockHeaderTestFixture().buildHeader());
when(protocolSchedule.getByBlockNumber(blockHeader.getNumber())).thenReturn(protocolSpec);
when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any(), any()))
.thenReturn(paths);
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) debugStandardTraceBadBlockToFile.response(request);
final List result = (ArrayList) response.getResult();

assertThat(result.size()).isEqualTo(1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.mockito.Mockito.when;

import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ImmutableTransactionTraceParams;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockBody;
Expand Down Expand Up @@ -113,6 +114,8 @@ public void setUp() throws Exception {
when(protocolSchedule.getByBlockNumber(12)).thenReturn(protocolSpec);
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager());
}

@Test
Expand Down Expand Up @@ -259,7 +262,6 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr
transactions,
Optional.of(ImmutableTransactionTraceParams.builder().build()),
traceDir.getRoot().toPath());
;

assertThat(transactionTraces.size()).isEqualTo(1);
assertThat(Files.readString(Path.of(transactionTraces.get(0))))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.ethereum.core.Hash;

import java.util.Collection;
import java.util.Optional;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
Expand Down Expand Up @@ -46,4 +47,14 @@ public void addBadBlock(final Block badBlock) {
public Collection<Block> getBadBlocks() {
return badBlocks.asMap().values();
}

/**
* Return an invalid block based on the hash
*
* @param hash of the block
* @return an invalid block
*/
public Optional<Block> getBadBlock(final Hash hash) {
return Optional.ofNullable(badBlocks.getIfPresent(hash));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,22 @@ public boolean isSuccessful() {
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}

@Test
public void shouldReturnBadBlockBasedOnTheHash() {
when(blockchain.getBlockHeader(any(Hash.class)))
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader()));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(false);
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
badBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlock(badBlock.getHash())).containsSame(badBlock);
}
}

0 comments on commit cfaccb6

Please sign in to comment.