[PAN-2946] Retesteth support (#1818)

Add support for retesteth, a JSON-RPC based reference testing API.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Danno Ferrin 5 years ago committed by GitHub
parent bb9c6538dc
commit 06a284817e
  1. 2
      ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/EthScheduler.java
  2. 2
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugAccountRange.java
  3. 2
      ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_blockHash.json
  4. 2
      ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_complete.json
  5. 2
      ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_partial.json
  6. 53
      ethereum/retesteth/build.gradle
  7. 83
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java
  8. 58
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/RetestethClock.java
  9. 28
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/RetestethConfiguration.java
  10. 252
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/RetestethContext.java
  11. 113
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/RetestethService.java
  12. 58
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestGetLogHash.java
  13. 78
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestImportRawBlock.java
  14. 87
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestMineBlocks.java
  15. 43
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestModifyTimestamp.java
  16. 43
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestRewindToBlock.java
  17. 142
      ethereum/retesteth/src/main/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestSetChainParams.java
  18. 83
      ethereum/retesteth/src/test/java/tech/pegasys/pantheon/ethereum/retesteth/methods/TestSetChainParamsTest.java
  19. 102
      ethereum/retesteth/src/test/resources/tech/pegasys/pantheon/ethereum/retesteth/methods/multimpleBalanceInstructionChainParams.json
  20. 1
      pantheon/build.gradle
  21. 2
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  22. 140
      pantheon/src/main/java/tech/pegasys/pantheon/cli/subcommands/RetestethSubCommand.java
  23. 1
      settings.gradle

@ -224,7 +224,7 @@ public class EthScheduler {
}
}
void awaitStop() throws InterruptedException {
public void awaitStop() throws InterruptedException {
shutdown.await();
serviceFutures.forEach(future -> future.cancel(true));
if (!syncWorkerExecutor.awaitTermination(30, TimeUnit.SECONDS)) {

@ -55,7 +55,7 @@ public class DebugAccountRange implements JsonRpcMethod {
public String getName() {
// TODO(shemnon) 5229b899 is the last stable commit of retesteth, after this they rename the
// method to just "debug_accountRange". Once the tool is stable we will support the new name.
return "debug_accountRangeAt";
return "debug_accountRange";
}
@Override

@ -2,7 +2,7 @@
"request": {
"id": 415,
"jsonrpc": "2.0",
"method": "debug_accountRangeAt",
"method": "debug_accountRange",
"params": [
"0xc8df1f061abb4d0c107b2b1a794ade8780b3120e681f723fe55a7be586d95ba6",
"0x1",

@ -2,7 +2,7 @@
"request": {
"id": 414,
"jsonrpc": "2.0",
"method": "debug_accountRangeAt",
"method": "debug_accountRange",
"params": [
"0x10",
"0x1",

@ -2,7 +2,7 @@
"request": {
"id": 415,
"jsonrpc": "2.0",
"method": "debug_accountRangeAt",
"method": "debug_accountRange",
"params": [
"0x10",
"0x1",

@ -0,0 +1,53 @@
/*
* Copyright 2019 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.
*/
apply plugin: 'java-library'
jar {
baseName 'pantheon-retesteth'
manifest {
attributes(
'Specification-Title': baseName,
'Specification-Version': project.version,
'Implementation-Title': baseName,
'Implementation-Version': calculateVersion()
)
}
}
dependencies {
implementation(project(':config'))
implementation(project(':ethereum:blockcreation'))
implementation(project(':ethereum:core'))
implementation(project(':ethereum:eth'))
implementation(project(':ethereum:graphql'))
implementation(project(':ethereum:jsonrpc'))
implementation(project(':ethereum:rlp'))
implementation(project(':ethereum:p2p'))
// implementation(project(':pantheon'))
implementation(project(':metrics:core'))
implementation(project(':nat'))
implementation(project(':services:kvstore'))
implementation(project(':util'))
implementation 'com.google.guava:guava'
implementation 'io.vertx:vertx-core'
implementation 'io.vertx:vertx-web'
implementation 'org.apache.logging.log4j:log4j-api'
testImplementation 'junit:junit'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'
runtime 'org.apache.logging.log4j:log4j-core'
}

@ -0,0 +1,83 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.MainnetBlockValidator;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.TransactionFilter;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.BlockProcessor;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockProcessor;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import java.math.BigInteger;
import java.util.Optional;
public class NoRewardProtocolScheduleWrapper<C> implements ProtocolSchedule<C> {
private final ProtocolSchedule<C> delegate;
NoRewardProtocolScheduleWrapper(final ProtocolSchedule<C> delegate) {
this.delegate = delegate;
}
@Override
public ProtocolSpec<C> getByBlockNumber(final long number) {
final ProtocolSpec<C> original = delegate.getByBlockNumber(number);
final BlockProcessor noRewardBlockProcessor =
new MainnetBlockProcessor(
original.getTransactionProcessor(),
original.getTransactionReceiptFactory(),
Wei.ZERO,
original.getMiningBeneficiaryCalculator(),
original.isSkipZeroBlockRewards());
final BlockValidator<C> noRewardBlockValidator =
new MainnetBlockValidator<>(
original.getBlockHeaderValidator(),
original.getBlockBodyValidator(),
noRewardBlockProcessor);
final BlockImporter<C> noRewardBlockImporter =
new MainnetBlockImporter<>(noRewardBlockValidator);
return new ProtocolSpec<>(
original.getName(),
original.getEvm(),
original.getTransactionValidator(),
original.getTransactionProcessor(),
original.getBlockHeaderValidator(),
original.getOmmerHeaderValidator(),
original.getBlockBodyValidator(),
noRewardBlockProcessor,
noRewardBlockImporter,
noRewardBlockValidator,
original.getBlockHeaderFunctions(),
original.getTransactionReceiptFactory(),
original.getDifficultyCalculator(),
Wei.ZERO, // block reward
original.getMiningBeneficiaryCalculator(),
original.getPrecompileContractRegistry(),
original.isSkipZeroBlockRewards());
}
@Override
public Optional<BigInteger> getChainId() {
return delegate.getChainId();
}
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
delegate.setTransactionFilter(transactionFilter);
}
}

@ -0,0 +1,58 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Optional;
public class RetestethClock extends Clock {
private Optional<Instant> fixedInstant;
private final Clock delegateClock;
RetestethClock() {
this(Clock.systemUTC());
}
private RetestethClock(final Clock delegateClock) {
fixedInstant = Optional.empty();
this.delegateClock = delegateClock;
}
@Override
public ZoneId getZone() {
return delegateClock.getZone();
}
@Override
public Clock withZone(final ZoneId zone) {
final RetestethClock zonedClock = new RetestethClock(delegateClock.withZone(zone));
zonedClock.fixedInstant = fixedInstant;
return zonedClock;
}
@Override
public Instant instant() {
return fixedInstant.orElseGet(delegateClock::instant);
}
public void resetTime(final long time) {
fixedInstant = Optional.of(Instant.ofEpochSecond(time));
}
public void advanceSeconds(final long seconds) {
fixedInstant = Optional.of(Instant.ofEpochSecond(instant().getEpochSecond() + seconds));
}
}

@ -0,0 +1,28 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth;
import java.nio.file.Path;
public class RetestethConfiguration {
private final Path dataPath;
public RetestethConfiguration(final Path dataPath) {
this.dataPath = dataPath;
}
Path getDataPath() {
return dataPath;
}
}

@ -0,0 +1,252 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth;
import static tech.pegasys.pantheon.config.JsonUtil.normalizeKeys;
import tech.pegasys.pantheon.config.JsonGenesisConfigOptions;
import tech.pegasys.pantheon.config.JsonUtil;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.blockcreation.IncrementingNonceGenerator;
import tech.pegasys.pantheon.ethereum.chain.DefaultMutableBlockchain;
import tech.pegasys.pantheon.ethereum.chain.GenesisState;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext;
import tech.pegasys.pantheon.ethereum.eth.manager.EthMessages;
import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers;
import tech.pegasys.pantheon.ethereum.eth.manager.EthScheduler;
import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPool;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPoolConfiguration;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPoolFactory;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries;
import tech.pegasys.pantheon.ethereum.mainnet.EthHashSolver;
import tech.pegasys.pantheon.ethereum.mainnet.EthHasher;
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class RetestethContext {
private static final Logger LOG = LogManager.getLogger();
private static final EthHasher NO_WORK_HASHER =
(final byte[] buffer, final long nonce, final long number, final byte[] headerHash) -> {};
private final ReentrantLock contextLock = new ReentrantLock();
private Address coinbase;
private DefaultMutableBlockchain blockchain;
private ProtocolContext<Void> protocolContext;
private BlockchainQueries blockchainQueries;
private ProtocolSchedule<Void> protocolSchedule;
private HeaderValidationMode headerValidationMode;
private BlockReplay blockReplay;
private RetestethClock retestethClock;
private TransactionPool transactionPool;
private EthScheduler ethScheduler;
private EthHashSolver ethHashSolver;
public boolean resetContext(
final String genesisConfigString, final String sealEngine, final Optional<Long> clockTime) {
contextLock.lock();
try {
tearDownContext();
return buildContext(genesisConfigString, sealEngine, clockTime);
} catch (final Exception e) {
LOG.error("Error shutting down existing runner", e);
return false;
} finally {
contextLock.unlock();
}
}
private void tearDownContext() {
try {
if (ethScheduler != null) {
ethScheduler.stop();
ethScheduler.awaitStop();
}
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
}
private boolean buildContext(
final String genesisConfigString, final String sealEngine, final Optional<Long> clockTime) {
final ObjectNode genesisConfig =
normalizeKeys(JsonUtil.objectNodeFromString(genesisConfigString));
retestethClock = new RetestethClock();
clockTime.ifPresent(retestethClock::resetTime);
final MetricsSystem metricsSystem = new NoOpMetricsSystem();
protocolSchedule =
MainnetProtocolSchedule.fromConfig(
JsonGenesisConfigOptions.fromJsonObject(
JsonUtil.getObjectNode(genesisConfig, "config").get()));
if ("NoReward".equalsIgnoreCase(sealEngine)) {
protocolSchedule = new NoRewardProtocolScheduleWrapper<>(protocolSchedule);
}
final GenesisState genesisState = GenesisState.fromJson(genesisConfigString, protocolSchedule);
coinbase = genesisState.getBlock().getHeader().getCoinbase();
final WorldStateArchive worldStateArchive =
new WorldStateArchive(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
final MutableWorldState worldState = worldStateArchive.getMutable();
genesisState.writeStateTo(worldState);
blockchain = createInMemoryBlockchain(genesisState.getBlock());
protocolContext = new ProtocolContext<>(blockchain, worldStateArchive, null);
blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive);
final String sealengine = JsonUtil.getString(genesisConfig, "sealengine", "");
headerValidationMode =
"NoProof".equals(sealengine) || "NoReward".equals(sealEngine)
? HeaderValidationMode.LIGHT
: HeaderValidationMode.FULL;
final Iterable<Long> nonceGenerator = new IncrementingNonceGenerator(0);
ethHashSolver =
("NoProof".equals(sealengine) || "NoReward".equals(sealEngine))
? new EthHashSolver(nonceGenerator, NO_WORK_HASHER)
: new EthHashSolver(nonceGenerator, new EthHasher.Light());
blockReplay =
new BlockReplay(
protocolSchedule,
blockchainQueries.getBlockchain(),
blockchainQueries.getWorldStateArchive());
// mining support
final EthPeers ethPeers = new EthPeers("reteseth", retestethClock, metricsSystem);
final SyncState syncState = new SyncState(blockchain, ethPeers);
ethScheduler = new EthScheduler(1, 1, 1, metricsSystem);
final EthContext ethContext = new EthContext(ethPeers, new EthMessages(), ethScheduler);
final TransactionPoolConfiguration transactionPoolConfiguration =
TransactionPoolConfiguration.builder().build();
transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
retestethClock,
metricsSystem,
syncState,
Wei.ZERO,
transactionPoolConfiguration);
LOG.trace("Genesis Block {} ", genesisState::getBlock);
return true;
}
private static DefaultMutableBlockchain createInMemoryBlockchain(final Block genesisBlock) {
return createInMemoryBlockchain(genesisBlock, new MainnetBlockHeaderFunctions());
}
private static DefaultMutableBlockchain createInMemoryBlockchain(
final Block genesisBlock, final BlockHeaderFunctions blockHeaderFunctions) {
final InMemoryKeyValueStorage keyValueStorage = new InMemoryKeyValueStorage();
return new DefaultMutableBlockchain(
genesisBlock,
new KeyValueStoragePrefixedKeyBlockchainStorage(keyValueStorage, blockHeaderFunctions),
new NoOpMetricsSystem());
}
public ProtocolSchedule<Void> getProtocolSchedule() {
return protocolSchedule;
}
public ProtocolContext<Void> getProtocolContext() {
return protocolContext;
}
public long getBlockHeight() {
return blockchain.getChainHeadBlockNumber();
}
public ProtocolSpec<Void> getProtocolSpec(final long blockNumber) {
return getProtocolSchedule().getByBlockNumber(blockNumber);
}
public BlockHeader getBlockHeader(final long blockNumber) {
return blockchain.getBlockHeader(blockNumber).get();
}
public BlockchainQueries getBlockchainQueries() {
return blockchainQueries;
}
public HeaderValidationMode getHeaderValidationMode() {
return headerValidationMode;
}
BlockReplay getBlockReplay() {
return blockReplay;
}
public TransactionPool getTransactionPool() {
return transactionPool;
}
PendingTransactions getPendingTransactions() {
return transactionPool.getPendingTransactions();
}
public Address getCoinbase() {
return coinbase;
}
public DefaultMutableBlockchain getBlockchain() {
return blockchain;
}
public RetestethClock getRetestethClock() {
return retestethClock;
}
public EthHashSolver getEthHashSolver() {
return ethHashSolver;
}
}

@ -0,0 +1,113 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcHttpService;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcMethodsFactory;
import tech.pegasys.pantheon.ethereum.jsonrpc.health.HealthService;
import tech.pegasys.pantheon.ethereum.jsonrpc.health.LivenessCheck;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugAccountRange;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugStorageRangeAt;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthBlockNumber;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBalance;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBlockByNumber;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetCode;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetTransactionCount;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthSendRawTransaction;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.Web3ClientVersion;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResultFactory;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestGetLogHash;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestImportRawBlock;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestMineBlocks;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestModifyTimestamp;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestRewindToBlock;
import tech.pegasys.pantheon.ethereum.retesteth.methods.TestSetChainParams;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import io.vertx.core.Vertx;
public class RetestethService {
private final JsonRpcHttpService jsonRpcHttpService;
private final Vertx vertx;
private final RetestethContext retestethContext;
public RetestethService(
final String clientVersion,
final RetestethConfiguration retestethConfiguration,
final JsonRpcConfiguration jsonRpcConfiguration) {
vertx = Vertx.vertx();
retestethContext = new RetestethContext();
final JsonRpcParameter parameters = new JsonRpcParameter();
final BlockResultFactory blockResult = new BlockResultFactory();
final Map<String, JsonRpcMethod> jsonRpcMethods = new HashMap<>();
JsonRpcMethodsFactory.addMethods(
jsonRpcMethods,
new Web3ClientVersion(clientVersion),
new TestSetChainParams(retestethContext),
new TestImportRawBlock(retestethContext, parameters),
new EthBlockNumber(retestethContext::getBlockchainQueries, true),
new EthGetBlockByNumber(
retestethContext::getBlockchainQueries, blockResult, parameters, true),
new DebugAccountRange(parameters, retestethContext::getBlockchainQueries),
new EthGetBalance(retestethContext::getBlockchainQueries, parameters),
new EthGetCode(retestethContext::getBlockchainQueries, parameters),
new EthGetTransactionCount(
retestethContext::getBlockchainQueries,
retestethContext::getPendingTransactions,
parameters,
true),
new DebugStorageRangeAt(
parameters,
retestethContext::getBlockchainQueries,
retestethContext::getBlockReplay,
true),
new TestModifyTimestamp(retestethContext, parameters),
new EthSendRawTransaction(retestethContext::getTransactionPool, parameters, true),
new TestMineBlocks(retestethContext, parameters),
new TestGetLogHash(retestethContext, parameters),
new TestRewindToBlock(retestethContext, parameters));
jsonRpcHttpService =
new JsonRpcHttpService(
vertx,
retestethConfiguration.getDataPath(),
jsonRpcConfiguration,
new NoOpMetricsSystem(),
Optional.empty(),
jsonRpcMethods,
new HealthService(new LivenessCheck()),
HealthService.ALWAYS_HEALTHY);
}
public void start() {
jsonRpcHttpService.start();
}
public void close() {
stop();
}
public void stop() {
jsonRpcHttpService.stop();
}
}

@ -0,0 +1,58 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.LogSeries;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.TransactionReceiptWithMetadata;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import java.util.Optional;
public class TestGetLogHash implements JsonRpcMethod {
private final RetestethContext context;
private final JsonRpcParameter parameters;
public TestGetLogHash(final RetestethContext context, final JsonRpcParameter parameters) {
this.context = context;
this.parameters = parameters;
}
@Override
public String getName() {
return "test_getLogHash";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
final Hash txHash = parameters.required(request.getParams(), 0, Hash.class);
final Optional<TransactionReceiptWithMetadata> receipt =
context.getBlockchainQueries().transactionReceiptByTransactionHash(txHash);
return new JsonRpcSuccessResponse(
request.getId(),
receipt.map(this::calculateLogHash).orElse(Hash.EMPTY_LIST_HASH).toString());
}
private Hash calculateLogHash(
final TransactionReceiptWithMetadata transactionReceiptWithMetadata) {
final LogSeries logs = new LogSeries(transactionReceiptWithMetadata.getReceipt().getLogs());
return Hash.hash(RLP.encode(logs::writeTo));
}
}

@ -0,0 +1,78 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPException;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestImportRawBlock implements JsonRpcMethod {
private static final Logger LOG = LogManager.getLogger();
private static final String METHOD_NAME = "test_importRawBlock";
private final RetestethContext context;
private final JsonRpcParameter parameters;
public TestImportRawBlock(final RetestethContext context, final JsonRpcParameter parameters) {
this.context = context;
this.parameters = parameters;
}
@Override
public String getName() {
return METHOD_NAME;
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
final String input = parameters.required(request.getParams(), 0, String.class);
final ProtocolSpec<Void> protocolSpec = context.getProtocolSpec(context.getBlockHeight());
final ProtocolContext<Void> protocolContext = this.context.getProtocolContext();
final Block block;
try {
block =
Block.readFrom(
RLP.input(BytesValue.fromHexString(input)), protocolSpec.getBlockHeaderFunctions());
} catch (final RLPException | IllegalArgumentException e) {
LOG.debug("Failed to parse block RLP", e);
return new JsonRpcSuccessResponse(request.getId(), "0x");
}
final BlockImporter<Void> blockImporter = protocolSpec.getBlockImporter();
if (blockImporter.importBlock(
protocolContext,
block,
context.getHeaderValidationMode(),
context.getHeaderValidationMode())) {
return new JsonRpcSuccessResponse(request.getId(), block.getHash().toString());
} else {
LOG.debug("Failed to import block.");
return new JsonRpcSuccessResponse(request.getId(), "0x");
}
}
}

@ -0,0 +1,87 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.blockcreation.EthHashBlockCreator;
import tech.pegasys.pantheon.ethereum.chain.DefaultMutableBlockchain;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethClock;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import com.google.common.base.Functions;
public class TestMineBlocks implements JsonRpcMethod {
private final RetestethContext context;
private final JsonRpcParameter parameters;
public TestMineBlocks(final RetestethContext context, final JsonRpcParameter parameters) {
this.context = context;
this.parameters = parameters;
}
@Override
public String getName() {
return "test_mineBlocks";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
long blocksToMine = parameters.required(request.getParams(), 0, Long.class);
while (blocksToMine-- > 0) {
if (!mineNewBlock()) {
return new JsonRpcSuccessResponse(request.getId(), false);
}
}
return new JsonRpcSuccessResponse(request.getId(), true);
}
private boolean mineNewBlock() {
final RetestethClock retesethClock = context.getRetestethClock();
final ProtocolSchedule<Void> protocolSchedule = context.getProtocolSchedule();
final ProtocolContext<Void> protocolContext = context.getProtocolContext();
final DefaultMutableBlockchain blockchain = context.getBlockchain();
final HeaderValidationMode headerValidationMode = context.getHeaderValidationMode();
final EthHashBlockCreator blockCreator =
new EthHashBlockCreator(
context.getCoinbase(),
header -> BytesValue.of(),
context.getTransactionPool().getPendingTransactions(),
protocolContext,
protocolSchedule,
Functions.identity(),
context.getEthHashSolver(),
Wei.ZERO,
blockchain.getChainHeadHeader());
final Block block = blockCreator.createBlock(retesethClock.instant().getEpochSecond());
// advance clock so next mine won't hit the same timestamp
retesethClock.advanceSeconds(1);
final BlockImporter<Void> blockImporter =
protocolSchedule.getByBlockNumber(blockchain.getChainHeadBlockNumber()).getBlockImporter();
return blockImporter.importBlock(
protocolContext, block, headerValidationMode, headerValidationMode);
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
public class TestModifyTimestamp implements JsonRpcMethod {
private final RetestethContext context;
private final JsonRpcParameter parameters;
public TestModifyTimestamp(final RetestethContext context, final JsonRpcParameter parameters) {
this.context = context;
this.parameters = parameters;
}
@Override
public String getName() {
return "test_modifyTimestamp";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
final long epochSeconds = parameters.required(request.getParams(), 0, Long.class);
context.getRetestethClock().resetTime(epochSeconds);
return new JsonRpcSuccessResponse(request.getId(), true);
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
public class TestRewindToBlock implements JsonRpcMethod {
private final RetestethContext context;
private final JsonRpcParameter parameters;
public TestRewindToBlock(final RetestethContext context, final JsonRpcParameter parameters) {
this.context = context;
this.parameters = parameters;
}
@Override
public String getName() {
return "test_rewindToBlock";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
final long blockNumber = parameters.required(request.getParams(), 0, Long.TYPE);
return new JsonRpcSuccessResponse(
request.getId(), context.getBlockchain().rewindToBlock(blockNumber));
}
}

@ -0,0 +1,142 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestSetChainParams implements JsonRpcMethod {
private static final Logger LOG = LogManager.getLogger();
public static final String METHOD_NAME = "test_setChainParams";
private final RetestethContext context;
public TestSetChainParams(final RetestethContext context) {
this.context = context;
}
@Override
public String getName() {
return METHOD_NAME;
}
@SuppressWarnings("unchecked")
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
try {
final JsonObject chainParamsAsJson =
new JsonObject((Map<String, Object>) request.getParams()[0]);
final String chainParamsAsString = chainParamsAsJson.encodePrettily();
LOG.trace("ChainParams {}", chainParamsAsString);
final String genesisFileAsString = modifyGenesisFile(chainParamsAsString);
LOG.trace("Genesis {}", genesisFileAsString);
final boolean result =
context.resetContext(
genesisFileAsString,
chainParamsAsJson.getString("sealEngine", "NoProof"),
Optional.empty());
return new JsonRpcSuccessResponse(request.getId(), result);
} catch (final Exception e) {
LOG.error("Unhandled error", e);
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS);
}
}
private static void maybeMove(
final JsonObject src, final String srcName, final JsonObject dest, final String destName) {
if (src.containsKey(srcName)) {
dest.put(destName, src.getValue(srcName));
src.remove(srcName);
}
}
private static void maybeMoveToNumber(
final JsonObject src, final String srcName, final JsonObject dest, final String destName) {
if (src.containsKey(srcName)) {
dest.put(destName, Long.decode(src.getString(srcName)));
src.remove(srcName);
}
}
private static void maybeMoveToNumber(
final JsonObject src,
final String srcName,
final JsonObject dest,
final String destName,
final long defaultValue) {
if (src.containsKey(srcName)) {
dest.put(destName, Long.decode(src.getString(srcName)));
src.remove(srcName);
} else {
dest.put(destName, defaultValue);
}
}
private static String modifyGenesisFile(final String initialGenesis) {
final JsonObject chainParamsJson = new JsonObject(initialGenesis);
final JsonObject config = new JsonObject();
chainParamsJson.put("config", config);
final JsonObject params = chainParamsJson.getJsonObject("params");
final JsonObject genesis = chainParamsJson.getJsonObject("genesis");
// Whether sealEngine is NoProof, Ethash, or NoReward the genesis file is the same
final JsonObject ethash = new JsonObject();
config.put("ethash", ethash);
maybeMoveToNumber(params, "homesteadForkBlock", config, "homesteadBlock");
maybeMoveToNumber(params, "EIP150ForkBlock", config, "eip150Block");
maybeMoveToNumber(params, "EIP158ForkBlock", config, "eip158Block");
maybeMoveToNumber(params, "byzantiumForkBlock", config, "byzantiumBlock");
maybeMoveToNumber(params, "constantinopleForkBlock", config, "constantinopleBlock");
maybeMoveToNumber(params, "constantinopleFixForkBlock", config, "constantinopleFixBlock");
maybeMoveToNumber(params, "istanbulForkBlock", config, "istanbulBlock");
maybeMoveToNumber(params, "chainID", config, "chainId", 1);
maybeMove(genesis, "author", chainParamsJson, "coinbase");
maybeMove(genesis, "difficulty", chainParamsJson, "difficulty");
maybeMove(genesis, "extraData", chainParamsJson, "extraData");
maybeMove(genesis, "gasLimit", chainParamsJson, "gasLimit");
maybeMove(genesis, "mixHash", chainParamsJson, "mixHash");
maybeMove(genesis, "nonce", chainParamsJson, "nonce");
maybeMove(genesis, "timestamp", chainParamsJson, "timestamp");
maybeMove(chainParamsJson, "accounts", chainParamsJson, "alloc");
// strip out precompiles with zero balance
final JsonObject alloc = chainParamsJson.getJsonObject("alloc");
final Iterator<String> fieldNamesIter = alloc.fieldNames().iterator();
while (fieldNamesIter.hasNext()) {
final String address = fieldNamesIter.next();
final JsonObject account = alloc.getJsonObject(address);
if (account.containsKey("precompiled") && !account.containsKey("balance")) {
fieldNamesIter.remove();
}
}
return chainParamsJson.encodePrettily();
}
}

@ -0,0 +1,83 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.ethereum.retesteth.methods;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethContext;
import java.io.IOException;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestSetChainParamsTest {
private static RetestethContext context;
private static TestSetChainParams test_setChainParams;
@BeforeClass
public static void setupClass() {
context = new RetestethContext();
test_setChainParams = new TestSetChainParams(context);
}
@Test
public void testValidateGenesisImport() throws IOException {
final String chainParamsJsonString =
Resources.toString(
TestSetChainParamsTest.class.getResource("multimpleBalanceInstructionChainParams.json"),
Charsets.UTF_8);
final JsonObject chainParamsJson = new JsonObject(chainParamsJsonString);
final JsonRpcRequest request =
new JsonRpcRequest(
"2.0", TestSetChainParams.METHOD_NAME, new Object[] {chainParamsJson.getMap()});
assertThat(test_setChainParams.response(request))
.isEqualTo(new JsonRpcSuccessResponse(null, true));
final BlockHeader blockHeader = context.getBlockHeader(0);
assertThat(blockHeader.getLogsBloom().toString())
.isEqualTo(
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
assertThat(blockHeader.getCoinbase().toString())
.isEqualTo("0x8888f1f195afa192cfee860698584c030f4c9db1");
assertThat(blockHeader.getDifficulty().toShortHexString()).isEqualTo("0x20000");
assertThat(blockHeader.getExtraData().toString()).isEqualTo("0x42");
assertThat(blockHeader.getGasLimit()).isEqualTo(3141592);
assertThat(blockHeader.getGasUsed()).isEqualTo(0);
assertThat(blockHeader.getMixHash().toString())
.isEqualTo("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
assertThat(blockHeader.getNonce()).isEqualTo(0x0102030405060708L);
assertThat(blockHeader.getNumber()).isEqualTo(0);
assertThat(blockHeader.getParentHash().toString())
.isEqualTo("0x0000000000000000000000000000000000000000000000000000000000000000");
assertThat(blockHeader.getReceiptsRoot().toString())
.isEqualTo("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
assertThat(blockHeader.getStateRoot().toString())
.isEqualTo("0xf403922bfd555a9223f68fc755564004e20d78bb42aae647e867e3b23c48beba");
assertThat(blockHeader.getTimestamp()).isEqualTo(0x54c98c81);
assertThat(blockHeader.getTransactionsRoot().toString())
.isEqualTo("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
assertThat(blockHeader.getOmmersHash().toString())
.isEqualTo("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347");
}
}

@ -0,0 +1,102 @@
{
"accounts": {
"0x0000000000000000000000000000000000000001": {
"precompiled": {
"linear": {
"base": 3000,
"word": 0
},
"name": "ecrecover"
}
},
"0x0000000000000000000000000000000000000002": {
"precompiled": {
"linear": {
"base": 60,
"word": 12
},
"name": "sha256"
}
},
"0x0000000000000000000000000000000000000003": {
"precompiled": {
"linear": {
"base": 600,
"word": 120
},
"name": "sha256"
}
},
"0x0000000000000000000000000000000000000004": {
"precompiled": {
"linear": {
"base": 15,
"word": 3
},
"name": "identity"
}
},
"0x0000000000000000000000000000000000000005": {
"precompiled": {
"name": "modexp"
}
},
"0x0000000000000000000000000000000000000006": {
"precompiled": {
"linear": {
"base": 500,
"word": 0
},
"name": "alt_bn128_G1_add"
}
},
"0x0000000000000000000000000000000000000007": {
"precompiled": {
"linear": {
"base": 40000,
"word": 0
},
"name": "alt_bn128_G1_mul"
}
},
"0x0000000000000000000000000000000000000008": {
"precompiled": {
"name": "alt_bn128_pairing_product"
}
},
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
"balance": "0x0186a0",
"code": "0x73a94f5374fce5edbc8e2a8697c15331677e6ebf0b31600055738888f1f195afa192cfee860698584c030f4c9db13160015573a94f5374fce5edbc8e2a8697c15331677e6ebf0b31600255738888f1f195afa192cfee860698584c030f4c9db13160035573095e7baea6a6c7c4c2dfeb977efac326af552d8731600555",
"nonce": "0x00",
"storage": {}
},
"0x195e7baea6a6c7c4c2dfeb977efac326af552d87": {
"balance": "0x0186a0",
"code": "0x73a94f5374fce5edbc8e2a8697c15331677e6ebf0b31600055738888f1f195afa192cfee860698584c030f4c9db13160015573a94f5374fce5edbc8e2a8697c15331677e6ebf0b31600255738888f1f195afa192cfee860698584c030f4c9db13160035573095e7baea6a6c7c4c2dfeb977efac326af552d873160045573195e7baea6a6c7c4c2dfeb977efac326af552d8731600555",
"nonce": "0x00",
"storage": {}
},
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x1748721582",
"code": "0x",
"nonce": "0x00",
"storage": {}
}
},
"genesis": {
"author": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty": "0x020000",
"extraData": "0x42",
"gasLimit": "0x2fefd8",
"mixHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce": "0x0102030405060708",
"timestamp": "0x54c98c81"
},
"params": {
"EIP150ForkBlock": "0x00",
"EIP158ForkBlock": "0x00",
"byzantiumForkBlock": "0x00",
"homesteadForkBlock": "0x00"
},
"sealEngine": "NoProof"
}

@ -40,6 +40,7 @@ dependencies {
implementation project(':ethereum:graphql')
implementation project(':ethereum:permissioning')
implementation project(':ethereum:p2p')
implementation project(':ethereum:retesteth')
implementation project(':ethereum:rlp')
implementation project(':metrics:core')
implementation project(':nat')

@ -48,6 +48,7 @@ import tech.pegasys.pantheon.cli.options.TransactionPoolOptions;
import tech.pegasys.pantheon.cli.subcommands.PasswordSubCommand;
import tech.pegasys.pantheon.cli.subcommands.PublicKeySubCommand;
import tech.pegasys.pantheon.cli.subcommands.PublicKeySubCommand.KeyLoader;
import tech.pegasys.pantheon.cli.subcommands.RetestethSubCommand;
import tech.pegasys.pantheon.cli.subcommands.blocks.BlocksSubCommand;
import tech.pegasys.pantheon.cli.subcommands.operator.OperatorSubCommand;
import tech.pegasys.pantheon.cli.subcommands.rlp.RLPSubCommand;
@ -688,6 +689,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
new PublicKeySubCommand(resultHandler.out(), getKeyLoader()));
commandLine.addSubcommand(
PasswordSubCommand.COMMAND_NAME, new PasswordSubCommand(resultHandler.out()));
commandLine.addSubcommand(RetestethSubCommand.COMMAND_NAME, new RetestethSubCommand());
commandLine.addSubcommand(
RLPSubCommand.COMMAND_NAME, new RLPSubCommand(resultHandler.out(), in));
commandLine.addSubcommand(

@ -0,0 +1,140 @@
/*
* Copyright 2019 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.
*/
package tech.pegasys.pantheon.cli.subcommands;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.MANDATORY_PATH_FORMAT_HELP;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataPath;
import static tech.pegasys.pantheon.cli.subcommands.RetestethSubCommand.COMMAND_NAME;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
import tech.pegasys.pantheon.PantheonInfo;
import tech.pegasys.pantheon.cli.custom.JsonRPCWhitelistHostsProperty;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethConfiguration;
import tech.pegasys.pantheon.ethereum.retesteth.RetestethService;
import java.net.InetAddress;
import java.nio.file.Path;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@Command(
name = COMMAND_NAME,
description = "Run a Retesteth compatible server for reference tests.",
mixinStandardHelpOptions = true)
public class RetestethSubCommand implements Runnable {
private static final Logger LOG = LogManager.getLogger();
public static final String COMMAND_NAME = "retesteth";
@CommandLine.Option(
names = {"--data-path"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = "The path to Pantheon data directory (default: ${DEFAULT-VALUE})")
final Path dataPath = getDefaultPantheonDataPath(this);
@Option(
names = {"--logging", "-l"},
paramLabel = "<LOG VERBOSITY LEVEL>",
description =
"Logging verbosity levels: OFF, FATAL, WARN, INFO, DEBUG, TRACE, ALL (default: INFO)")
private final Level logLevel = Level.INFO;
@SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final.
@Option(
names = {"--rpc-http-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host for JSON-RPC HTTP to listen on (default: ${DEFAULT-VALUE})",
arity = "1")
private String rpcHttpHost = autoDiscoverDefaultIP().getHostAddress();
@Option(
names = {"--rpc-http-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port for JSON-RPC HTTP to listen on (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer rpcHttpPort = DEFAULT_JSON_RPC_PORT;
@Option(
names = {"--host-whitelist"},
paramLabel = "<hostname>[,<hostname>...]... or * or all",
description =
"Comma separated list of hostnames to whitelist for RPC access, or * to accept any host (default: ${DEFAULT-VALUE})",
defaultValue = "localhost,127.0.0.1")
private final JsonRPCWhitelistHostsProperty hostsWhitelist = new JsonRPCWhitelistHostsProperty();
private InetAddress autoDiscoveredDefaultIP;
// Used to discover the default IP of the client.
// Loopback IP is used by default as this is how smokeTests require it to be
// and it's probably a good security behaviour to default only on the localhost.
private InetAddress autoDiscoverDefaultIP() {
if (autoDiscoveredDefaultIP != null) {
return autoDiscoveredDefaultIP;
}
autoDiscoveredDefaultIP = InetAddress.getLoopbackAddress();
return autoDiscoveredDefaultIP;
}
private void prepareLogging() {
// set log level per CLI flags
if (logLevel != null) {
System.out.println("Setting logging level to " + logLevel.name());
Configurator.setAllLevels("", logLevel);
}
}
@Override
public void run() {
prepareLogging();
final RetestethConfiguration retestethConfiguration = new RetestethConfiguration(dataPath);
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setHost(rpcHttpHost);
jsonRpcConfiguration.setPort(rpcHttpPort);
jsonRpcConfiguration.setHostsWhitelist(hostsWhitelist);
final RetestethService retestethService =
new RetestethService(PantheonInfo.version(), retestethConfiguration, jsonRpcConfiguration);
Runtime.getRuntime()
.addShutdownHook(
new Thread(
() -> {
try {
retestethService.close();
LogManager.shutdown();
} catch (final Exception e) {
LOG.error("Failed to stop Pantheon Retesteth");
}
}));
retestethService.start();
try {
Thread.sleep(Long.MAX_VALUE); // Is there a better way?
} catch (final InterruptedException e) {
// e.printStackTrace();
}
}
}

@ -30,6 +30,7 @@ include 'ethereum:mock-p2p'
include 'ethereum:p2p'
include 'ethereum:permissioning'
include 'ethereum:referencetests'
include 'ethereum:retesteth'
include 'ethereum:rlp'
include 'ethereum:trie'
include 'metrics:core'

Loading…
Cancel
Save