Skip to content

Commit

Permalink
[PIE-1805] Add test cases for trace_replayBlockTransactions (PegaSysE…
Browse files Browse the repository at this point in the history
…ng#1881)

* Add chain data resources

* Update tests for tracing to run on custom data, add test cases

* Regenerate test cases, update tests to be agnostic on key ordering

* Remove dead code

* Move to-be-implemented test cases to root trace directory
  • Loading branch information
mbaxter authored and pscott committed Sep 2, 2019
1 parent 5e61d99 commit 0586e2f
Show file tree
Hide file tree
Showing 20 changed files with 3,768 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,58 @@ public int blockCount() {
}

public static BlockchainSetupUtil<Void> forTesting() {
return createEthashChain(BlockTestUtil.getTestChainResources());
return createForEthashChain(BlockTestUtil.getTestChainResources());
}

public static BlockchainSetupUtil<Void> forMainnet() {
return createEthashChain(BlockTestUtil.getMainnetResources());
return createForEthashChain(BlockTestUtil.getMainnetResources());
}

public static BlockchainSetupUtil<Void> forOutdatedFork() {
return createEthashChain(BlockTestUtil.getOutdatedForkResources());
return createForEthashChain(BlockTestUtil.getOutdatedForkResources());
}

public static BlockchainSetupUtil<Void> forUpgradedFork() {
return createEthashChain(BlockTestUtil.getUpgradedForkResources());
return createForEthashChain(BlockTestUtil.getUpgradedForkResources());
}

private static BlockchainSetupUtil<Void> createEthashChain(final ChainResources chainResources) {
public static BlockchainSetupUtil<Void> createForEthashChain(
final ChainResources chainResources) {
return create(
chainResources,
BlockchainSetupUtil::mainnetProtocolScheduleProvider,
BlockchainSetupUtil::mainnetProtocolContextProvider);
}

private static ProtocolSchedule<Void> mainnetProtocolScheduleProvider(
final GenesisConfigFile genesisConfigFile) {
return MainnetProtocolSchedule.fromConfig(genesisConfigFile.getConfigOptions());
}

private static ProtocolContext<Void> mainnetProtocolContextProvider(
final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive) {
return new ProtocolContext<>(blockchain, worldStateArchive, null);
}

private static <T> BlockchainSetupUtil<T> create(
final ChainResources chainResources,
final ProtocolScheduleProvider<T> protocolScheduleProvider,
final ProtocolContextProvider<T> protocolContextProvider) {
final TemporaryFolder temp = new TemporaryFolder();
try {
temp.create();
final String genesisJson = Resources.toString(chainResources.getGenesisURL(), Charsets.UTF_8);

final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisJson);
final ProtocolSchedule<Void> protocolSchedule =
MainnetProtocolSchedule.fromConfig(genesisConfigFile.getConfigOptions());
final ProtocolSchedule<T> protocolSchedule = protocolScheduleProvider.get(genesisConfigFile);

final GenesisState genesisState = GenesisState.fromJson(genesisJson, protocolSchedule);
final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
final WorldStateArchive worldArchive = createInMemoryWorldStateArchive();

genesisState.writeStateTo(worldArchive.getMutable());
final ProtocolContext<Void> protocolContext =
new ProtocolContext<>(blockchain, worldArchive, null);
final ProtocolContext<T> protocolContext =
protocolContextProvider.get(blockchain, worldArchive);

final Path blocksPath = Path.of(chainResources.getBlocksURL().toURI());
final List<Block> blocks = new ArrayList<>();
Expand All @@ -137,7 +157,7 @@ private static BlockchainSetupUtil<Void> createEthashChain(final ChainResources
blocks.add(iterator.next());
}
}
return new BlockchainSetupUtil<>(
return new BlockchainSetupUtil<T>(
genesisState, blockchain, protocolContext, protocolSchedule, worldArchive, blocks);
} catch (final IOException | URISyntaxException ex) {
throw new IllegalStateException(ex);
Expand Down Expand Up @@ -186,4 +206,12 @@ private void importBlocks(final List<Block> blocks) {
}
this.maxBlockNumber = blockchain.getChainHeadBlockNumber();
}

private interface ProtocolScheduleProvider<T> {
ProtocolSchedule<T> get(GenesisConfigFile genesisConfig);
}

private interface ProtocolContextProvider<T> {
ProtocolContext<T> get(MutableBlockchain blockchain, WorldStateArchive worldStateArchive);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods;

import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.debug.TraceOptions;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
Expand Down Expand Up @@ -58,6 +59,11 @@ protected Object resultByBlockNumber(final JsonRpcRequest request, final long bl
final TraceTypeParameter traceTypeParameter =
getParameters().required(request.getParams(), 1, TraceTypeParameter.class);

if (blockNumber == BlockHeader.GENESIS_BLOCK_NUMBER) {
// Nothing to trace for the genesis block
return emptyResult();
}

return getBlockchainQueries()
.getBlockchain()
.getBlockByNumber(blockNumber)
Expand Down Expand Up @@ -90,4 +96,9 @@ private JsonNode formatTrace(
ObjectMapper mapper = new ObjectMapper();
return mapper.createObjectNode();
}

private Object emptyResult() {
ObjectMapper mapper = new ObjectMapper();
return mapper.createArrayNode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
import java.util.List;
import java.util.stream.Stream;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
Expand All @@ -40,6 +41,7 @@
@RunWith(Parameterized.class)
public abstract class AbstractJsonRpcHttpBySpecTest extends AbstractJsonRpcHttpServiceTest {

private static ObjectMapper objectMapper = new ObjectMapper();
private final URL specURL;

public AbstractJsonRpcHttpBySpecTest(final String specName, final URL specURL) {
Expand Down Expand Up @@ -70,7 +72,7 @@ public static Object[][] findSpecFiles(final String... subDirectoryPaths) {
} catch (final URISyntaxException e) {
throw new RuntimeException("Problem converting URL to URI " + url, e);
}
try (final Stream<Path> s = Files.walk(dir)) {
try (final Stream<Path> s = Files.walk(dir, 1)) {
s.map(Path::toFile)
.filter(f -> f.getPath().endsWith(".json"))
.map(AbstractJsonRpcHttpBySpecTest::fileToParams)
Expand Down Expand Up @@ -98,18 +100,50 @@ private static Object[] fileToParams(final File file) {

private void jsonRPCCall(final URL specFile) throws IOException {
final String json = Resources.toString(specFile, Charsets.UTF_8);
final JsonObject spec = new JsonObject(json);
final ObjectNode specNode = (ObjectNode) objectMapper.readTree(json);

final String rawRequestBody = spec.getJsonObject("request").toString();
final String rawRequestBody = specNode.get("request").toString();
final RequestBody requestBody = RequestBody.create(JSON, rawRequestBody);
final Request request = new Request.Builder().post(requestBody).url(baseUrl).build();

try (final Response resp = client.newCall(request).execute()) {
final int expectedStatusCode = spec.getInteger("statusCode");
final int expectedStatusCode = specNode.get("statusCode").asInt();
assertThat(resp.code()).isEqualTo(expectedStatusCode);

final String expectedRespBody = spec.getJsonObject("response").encodePrettily();
assertThat(resp.body().string()).isEqualTo(expectedRespBody);
final ObjectNode responseBody;
try {
responseBody = (ObjectNode) objectMapper.readTree(resp.body().string());
} catch (Exception e) {
throw new RuntimeException("Unable to parse response as json object", e);
}

final ObjectNode expectedResponse = (ObjectNode) specNode.get("response");

// Check id
final String actualId = responseBody.get("id").toString();
final String expectedId = expectedResponse.get("id").toString();
assertThat(actualId).isEqualTo(expectedId);

// Check version
final String actualVersion = responseBody.get("jsonrpc").toString();
final String expectedVersion = expectedResponse.get("jsonrpc").toString();
assertThat(actualVersion).isEqualTo(expectedVersion);

// Check result
if (expectedResponse.has("result")) {
assertThat(responseBody.has("result")).isTrue();
final String expectedResult = expectedResponse.get("result").toString();
final String actualResult = responseBody.get("result").toString();
assertThat(actualResult).isEqualToIgnoringWhitespace(expectedResult);
}

// Check error
if (expectedResponse.has("error")) {
assertThat(responseBody.has("error")).isTrue();
final String expectedError = expectedResponse.get("error").toString();
final String actualError = responseBody.get("error").toString();
assertThat(actualError).isEqualToIgnoringWhitespace(expectedError);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc;

import static com.google.common.base.Preconditions.checkArgument;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -40,7 +41,9 @@
import tech.pegasys.pantheon.ethereum.p2p.rlpx.wire.Capability;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.testutil.BlockTestUtil.ChainResources;

import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
Expand Down Expand Up @@ -88,6 +91,15 @@ protected BlockchainSetupUtil<Void> getBlockchainSetupUtil() {
return BlockchainSetupUtil.forTesting();
}

protected BlockchainSetupUtil<Void> createBlockchainSetupUtil(
final String genesisPath, final String blocksPath) {
final URL genesisURL = AbstractJsonRpcHttpServiceTest.class.getResource(genesisPath);
final URL blocksURL = AbstractJsonRpcHttpServiceTest.class.getResource(blocksPath);
checkArgument(genesisURL != null, "Unable to locate genesis file: " + genesisPath);
checkArgument(blocksURL != null, "Unable to locate blocks file: " + blocksPath);
return BlockchainSetupUtil.createForEthashChain(new ChainResources(genesisURL, blocksURL));
}

@Before
public void setup() throws Exception {
setupBlockchain();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public void setup() throws Exception {

@Override
protected BlockchainSetupUtil<Void> getBlockchainSetupUtil() {
return BlockchainSetupUtil.forMainnet();
return createBlockchainSetupUtil(
"trace/chain-data/genesis.json", "trace/chain-data/blocks.bin");
}

@Override
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"config": {
"homesteadBlock": 0,
"daoForkBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"eip160Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"constantinopleFixBlock": 0,
"ethash": {
"fixeddifficulty": 15
},
"chainID": 1982,
"networkID": 1982
},
"nonce": "0x0000000000000042",
"gasLimit": "0x100000000000",
"difficulty": "0xf",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0010000000000000000000000000000000000000": {
"comment": "Add smart contract that will simply set 2 key-value pairs. Parses tx input into 4 32 byte values: [key1, val1, key2, val2]. Can be used to test constantinople fix.",
"code": "0x6020356000355560603560403555",
"balance": "0x0"
},
"0020000000000000000000000000000000000000": {
"comment": "Reads a single address from tx input, self-destructs and sends refund to input address.",
"code": "0x600035FF",
"balance": "0x300"
},
"0030000000000000000000000000000000000000": {
"comment": "Reads a 32 byte value from input data, increments it, and returns.",
"code": "0x60003560010160005260206000F3",
"balance": "0x0"
},
"0040000000000000000000000000000000000000": {
"comment": "Proxy a call to the address in the first 32 bytes, sending the rest of the input data to this address. Return 32 bytes from sub-call.",
"comment": "0x outSize 6020 outOffset 6000 inputSize 60203603 inputToMem(dupSize 80)6020600037 inOffset 6000 val 34 to 600035 gas 5A call F1 Return 60206000F3",
"code": "0x60206000602036038060206000376000346000355AF160206000F3",
"balance": "0x0"
},
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
"secretKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xf0000000000000000000000000000000000000000"
},
"627306090abaB3A6e1400e9345bC60c78a8BEf57": {
"secretKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xf0000000000000000000000"
},
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
"secretKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xf0000000000000000000000"
}
}
}
Loading

0 comments on commit 0586e2f

Please sign in to comment.