mirror of https://github.com/hyperledger/besu
EvmTool - A CLI for ad hoc performance and fuzz testing (#786)
A standalone CLI that performs EVM execution with provided genesis files and raw EVM bytecodes. Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>pull/812/head
parent
0cf6bd9aad
commit
912d125117
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
apply plugin: 'java-library' |
||||||
|
apply plugin: 'application' |
||||||
|
apply plugin: 'idea' |
||||||
|
|
||||||
|
jar { |
||||||
|
baseName 'besu-evmtool' |
||||||
|
manifest { |
||||||
|
attributes( |
||||||
|
'Specification-Title': baseName, |
||||||
|
'Specification-Version': project.version, |
||||||
|
'Implementation-Title': baseName, |
||||||
|
'Implementation-Version': calculateVersion() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation project(':besu') |
||||||
|
implementation project(':config') |
||||||
|
implementation project(':crypto') |
||||||
|
implementation project(':consensus:clique') |
||||||
|
implementation project(':consensus:ibft') |
||||||
|
implementation project(':ethereum:core') |
||||||
|
implementation project(':metrics:core') |
||||||
|
implementation project(':services:kvstore') |
||||||
|
implementation project(':util') |
||||||
|
|
||||||
|
implementation 'com.google.dagger:dagger' |
||||||
|
implementation 'com.google.guava:guava' |
||||||
|
implementation 'info.picocli:picocli' |
||||||
|
implementation 'io.vertx:vertx-core' |
||||||
|
implementation 'org.apache.logging.log4j:log4j-api' |
||||||
|
implementation 'org.apache.logging.log4j:log4j-core' |
||||||
|
|
||||||
|
annotationProcessor 'com.google.dagger:dagger-compiler' |
||||||
|
} |
||||||
|
|
||||||
|
mainClassName = 'org.hyperledger.besu.evmtool.EvmTool' |
||||||
|
|
||||||
|
startScripts { |
||||||
|
applicationName = 'evm' |
||||||
|
doLast { |
||||||
|
unixScript.text = unixScript.text.replace('BESU_HOME', '\$APP_HOME') |
||||||
|
windowsScript.text = windowsScript.text.replace('BESU_HOME', '%~dp0..') |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||||
|
import org.hyperledger.besu.ethereum.chain.BlockchainStorage; |
||||||
|
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; |
||||||
|
import org.hyperledger.besu.ethereum.core.Block; |
||||||
|
import org.hyperledger.besu.ethereum.core.MutableWorldView; |
||||||
|
import org.hyperledger.besu.ethereum.core.WorldUpdater; |
||||||
|
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage; |
||||||
|
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage; |
||||||
|
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; |
||||||
|
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; |
||||||
|
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; |
||||||
|
import org.hyperledger.besu.plugin.services.MetricsSystem; |
||||||
|
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||||
|
|
||||||
|
import javax.inject.Named; |
||||||
|
import javax.inject.Singleton; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@Module(includes = {GenesisFileModule.class, DataStoreModule.class}) |
||||||
|
public class BlockchainModule { |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
Blockchain provideBlockchain( |
||||||
|
@Named("GenesisBlock") final Block genesisBlock, |
||||||
|
final BlockchainStorage blockchainStorage, |
||||||
|
final MetricsSystem metricsSystem) { |
||||||
|
return DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, metricsSystem); |
||||||
|
} |
||||||
|
|
||||||
|
@Provides |
||||||
|
MutableWorldView getMutableWorldView( |
||||||
|
final WorldStateStorage worldStateStorage, |
||||||
|
final WorldStatePreimageStorage worldStatePreimageStorage) { |
||||||
|
return new DefaultMutableWorldState(worldStateStorage, worldStatePreimageStorage); |
||||||
|
} |
||||||
|
|
||||||
|
@Provides |
||||||
|
WorldStateStorage provideWorldStateStorage(final KeyValueStorage keyValueStorage) { |
||||||
|
return new WorldStateKeyValueStorage(keyValueStorage); |
||||||
|
} |
||||||
|
|
||||||
|
@Provides |
||||||
|
WorldStatePreimageStorage provideWorldStatePreimageStorage( |
||||||
|
final KeyValueStorage keyValueStorage) { |
||||||
|
return new WorldStatePreimageKeyValueStorage(keyValueStorage); |
||||||
|
} |
||||||
|
|
||||||
|
@Provides |
||||||
|
WorldUpdater provideWorldUpdater(final MutableWorldView mutableWorldView) { |
||||||
|
return mutableWorldView.updater(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.consensus.clique.CliqueProtocolSchedule; |
||||||
|
import org.hyperledger.besu.consensus.ibft.IbftBlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
|
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
class CliqueGenesisFileModule extends GenesisFileModule { |
||||||
|
|
||||||
|
CliqueGenesisFileModule(final String genesisConfig) { |
||||||
|
super(genesisConfig); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
ProtocolSchedule<?> provideProtocolSchedule( |
||||||
|
final GenesisConfigOptions configOptions, |
||||||
|
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) { |
||||||
|
// dagger can handle this magic one day
|
||||||
|
return CliqueProtocolSchedule.create(configOptions, null, revertReasonEnabled); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
BlockHeaderFunctions blockHashFunction() { |
||||||
|
return IbftBlockHeaderFunctions.forOnChainBlock(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.chain.BlockchainStorage; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; |
||||||
|
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@Module(includes = GenesisFileModule.class) |
||||||
|
public class DataStoreModule { |
||||||
|
|
||||||
|
@Provides |
||||||
|
@SuppressWarnings("CloseableProvides") |
||||||
|
KeyValueStorage provideKeyValueStorage() { |
||||||
|
throw new RuntimeException("Abstract"); |
||||||
|
} |
||||||
|
|
||||||
|
@Provides |
||||||
|
static BlockchainStorage provideBlockchainStorage( |
||||||
|
final KeyValueStorage keyValueStorage, final BlockHeaderFunctions blockHashFunction) { |
||||||
|
return new KeyValueStoragePrefixedKeyBlockchainStorage(keyValueStorage, blockHashFunction); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import static picocli.CommandLine.defaultExceptionHandler; |
||||||
|
|
||||||
|
import picocli.CommandLine; |
||||||
|
|
||||||
|
public final class EvmTool { |
||||||
|
private static final int SUCCESS_EXIT_CODE = 0; |
||||||
|
private static final int ERROR_EXIT_CODE = 1; |
||||||
|
|
||||||
|
public static void main(final String... args) { |
||||||
|
|
||||||
|
final EvmToolCommand evmToolCommand = new EvmToolCommand(); |
||||||
|
|
||||||
|
evmToolCommand.parse( |
||||||
|
new CommandLine.RunLast().andExit(SUCCESS_EXIT_CODE), |
||||||
|
defaultExceptionHandler().andExit(ERROR_EXIT_CODE), |
||||||
|
args); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,285 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.cli.config.NetworkName; |
||||||
|
import org.hyperledger.besu.ethereum.core.Account; |
||||||
|
import org.hyperledger.besu.ethereum.core.Address; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; |
||||||
|
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||||
|
import org.hyperledger.besu.ethereum.core.Gas; |
||||||
|
import org.hyperledger.besu.ethereum.core.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.core.LogsBloomFilter; |
||||||
|
import org.hyperledger.besu.ethereum.core.Wei; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; |
||||||
|
import org.hyperledger.besu.ethereum.vm.Code; |
||||||
|
import org.hyperledger.besu.ethereum.vm.EVM; |
||||||
|
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason; |
||||||
|
import org.hyperledger.besu.ethereum.vm.MessageFrame; |
||||||
|
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltException; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.time.Instant; |
||||||
|
import java.util.ArrayDeque; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import com.google.common.base.Stopwatch; |
||||||
|
import io.vertx.core.json.JsonArray; |
||||||
|
import io.vertx.core.json.JsonObject; |
||||||
|
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 org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.apache.tuweni.units.bigints.UInt256; |
||||||
|
import picocli.CommandLine; |
||||||
|
import picocli.CommandLine.Command; |
||||||
|
import picocli.CommandLine.Option; |
||||||
|
|
||||||
|
@Command( |
||||||
|
description = "This command evaluates EVM transactions.", |
||||||
|
abbreviateSynopsis = true, |
||||||
|
name = "evm", |
||||||
|
mixinStandardHelpOptions = true, |
||||||
|
sortOptions = false, |
||||||
|
header = "Usage:", |
||||||
|
synopsisHeading = "%n", |
||||||
|
descriptionHeading = "%nDescription:%n%n", |
||||||
|
optionListHeading = "%nOptions:%n", |
||||||
|
footerHeading = "%n", |
||||||
|
footer = "Hyperledger Besu is licensed under the Apache License 2.0") |
||||||
|
public class EvmToolCommand implements Runnable { |
||||||
|
|
||||||
|
private static final Logger LOG = LogManager.getLogger(); |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--code"}, |
||||||
|
paramLabel = "<code>", |
||||||
|
description = "code to be executed") |
||||||
|
private final Bytes codeHexString = Bytes.EMPTY; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--gas"}, |
||||||
|
paramLabel = "<int>") |
||||||
|
private final Gas gas = Gas.of(10_000_000_000L); |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--price"}, |
||||||
|
paramLabel = "<int>") |
||||||
|
private final Wei gasPriceGWei = Wei.ZERO; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--sender"}, |
||||||
|
paramLabel = "<address>", |
||||||
|
description = "address of ORIGIN") |
||||||
|
private final Address sender = Address.fromHexString("0x00"); |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--receiver"}, |
||||||
|
paramLabel = "<address>", |
||||||
|
description = "address of ADDRESS") |
||||||
|
private final Address receiver = Address.fromHexString("0x00"); |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--input"}, |
||||||
|
paramLabel = "<code>", |
||||||
|
description = "CALLDATA") |
||||||
|
private final Bytes callData = Bytes.EMPTY; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--value"}, |
||||||
|
paramLabel = "<int>") |
||||||
|
private final Wei ethValue = Wei.ZERO; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--json"}, |
||||||
|
description = "output json output for each opcode") |
||||||
|
private final Boolean showJsonResults = false; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--nomemory"}, |
||||||
|
description = "disable showing the full memory output for each op") |
||||||
|
private final Boolean showMemory = true; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--prestate", "--genesis"}, |
||||||
|
description = "a chain specification, the same one that the client normally would use") |
||||||
|
private final File genesisFile = null; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--chain"}, |
||||||
|
description = "Name of a well know chain") |
||||||
|
private final NetworkName network = null; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--repeat"}, |
||||||
|
description = "Number of times to repeat before gathering timing") |
||||||
|
private final Integer repeat = 0; |
||||||
|
|
||||||
|
private final EvmToolCommandOptionsModule daggerOptions = new EvmToolCommandOptionsModule(); |
||||||
|
|
||||||
|
void parse( |
||||||
|
final CommandLine.AbstractParseResultHandler<List<Object>> resultHandler, |
||||||
|
final CommandLine.DefaultExceptionHandler<List<Object>> exceptionHandler, |
||||||
|
final String[] args) { |
||||||
|
|
||||||
|
final CommandLine commandLine = new CommandLine(this); |
||||||
|
commandLine.addMixin("Dagger Options", daggerOptions); |
||||||
|
|
||||||
|
// add sub commands here
|
||||||
|
|
||||||
|
commandLine.registerConverter(Address.class, Address::fromHexString); |
||||||
|
commandLine.registerConverter(Bytes.class, Bytes::fromHexString); |
||||||
|
commandLine.registerConverter(Gas.class, (arg) -> Gas.of(Long.parseUnsignedLong(arg))); |
||||||
|
commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg))); |
||||||
|
|
||||||
|
commandLine.parseWithHandlers(resultHandler, exceptionHandler, args); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try { |
||||||
|
final EvmToolComponent component = |
||||||
|
DaggerEvmToolComponent.builder() |
||||||
|
.dataStoreModule(new InMemoryDataStoreModule()) |
||||||
|
.genesisFileModule( |
||||||
|
network == null |
||||||
|
? genesisFile == null |
||||||
|
? GenesisFileModule.createGenesisModule(NetworkName.DEV) |
||||||
|
: GenesisFileModule.createGenesisModule(genesisFile) |
||||||
|
: GenesisFileModule.createGenesisModule(network)) |
||||||
|
.evmToolCommandOptionsModule(daggerOptions) |
||||||
|
.metricsSystemModule(new PrometheusMetricsSystemModule()) |
||||||
|
.build(); |
||||||
|
|
||||||
|
final BlockHeader blockHeader = |
||||||
|
BlockHeaderBuilder.create() |
||||||
|
.parentHash(Hash.EMPTY) |
||||||
|
.coinbase(Address.ZERO) |
||||||
|
.difficulty(Difficulty.ONE) |
||||||
|
.number(1) |
||||||
|
.gasLimit(5000) |
||||||
|
.timestamp(Instant.now().toEpochMilli()) |
||||||
|
.ommersHash(Hash.EMPTY_LIST_HASH) |
||||||
|
.stateRoot(Hash.EMPTY_TRIE_HASH) |
||||||
|
.transactionsRoot(Hash.EMPTY) |
||||||
|
.receiptsRoot(Hash.EMPTY) |
||||||
|
.logsBloom(LogsBloomFilter.empty()) |
||||||
|
.gasUsed(0) |
||||||
|
.extraData(Bytes.EMPTY) |
||||||
|
.mixHash(Hash.EMPTY) |
||||||
|
.nonce(0) |
||||||
|
.blockHeaderFunctions(new MainnetBlockHeaderFunctions()) |
||||||
|
.buildBlockHeader(); |
||||||
|
|
||||||
|
Configurator.setAllLevels("", repeat == 0 ? Level.INFO : Level.OFF); |
||||||
|
int repeat = this.repeat; |
||||||
|
do { |
||||||
|
final boolean lastLoop = repeat == 0; |
||||||
|
final MessageFrame messageFrame = |
||||||
|
MessageFrame.builder() |
||||||
|
.type(MessageFrame.Type.MESSAGE_CALL) |
||||||
|
.messageFrameStack(new ArrayDeque<>()) |
||||||
|
.blockchain(component.getBlockchain()) |
||||||
|
.worldState(component.getWorldUpdater()) |
||||||
|
.initialGas(gas) |
||||||
|
.contract(Address.ZERO) |
||||||
|
.address(receiver) |
||||||
|
.originator(sender) |
||||||
|
.gasPrice(gasPriceGWei) |
||||||
|
.inputData(callData) |
||||||
|
.sender(Address.ZERO) |
||||||
|
.value(ethValue) |
||||||
|
.apparentValue(ethValue) |
||||||
|
.code(new Code(codeHexString)) |
||||||
|
.blockHeader(blockHeader) |
||||||
|
.depth(0) |
||||||
|
.completer(c -> {}) |
||||||
|
.miningBeneficiary(blockHeader.getCoinbase()) |
||||||
|
.blockHashLookup(new BlockHashLookup(blockHeader, component.getBlockchain())) |
||||||
|
.contractAccountVersion(Account.DEFAULT_VERSION) |
||||||
|
.build(); |
||||||
|
|
||||||
|
messageFrame.setState(MessageFrame.State.CODE_EXECUTING); |
||||||
|
final EVM evm = component.getEvmAtBlock().apply(0); |
||||||
|
|
||||||
|
final Stopwatch stopwatch = Stopwatch.createStarted(); |
||||||
|
evm.runToHalt( |
||||||
|
messageFrame, |
||||||
|
(frame, currentGasCost, executeOperation) -> { |
||||||
|
if (showJsonResults && lastLoop) { |
||||||
|
System.out.println(createEvmTraceOperation(messageFrame)); |
||||||
|
} |
||||||
|
executeOperation.execute(); |
||||||
|
}); |
||||||
|
stopwatch.stop(); |
||||||
|
|
||||||
|
if (lastLoop) { |
||||||
|
System.out.println( |
||||||
|
new JsonObject() |
||||||
|
.put( |
||||||
|
"gasUser", |
||||||
|
gas.minus(messageFrame.getRemainingGas()).asUInt256().toShortHexString()) |
||||||
|
.put("timens", stopwatch.elapsed().toNanos()) |
||||||
|
.put("time", stopwatch.elapsed().toNanos() / 1000)); |
||||||
|
} |
||||||
|
} while (repeat-- > 0); |
||||||
|
|
||||||
|
} catch (final IOException | ExceptionalHaltException e) { |
||||||
|
LOG.fatal(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private JsonObject createEvmTraceOperation(final MessageFrame messageFrame) { |
||||||
|
final JsonArray stack = new JsonArray(); |
||||||
|
for (int i = 0; i < messageFrame.stackSize(); i++) { |
||||||
|
stack.add(messageFrame.getStackItem(i).toShortHexString()); |
||||||
|
} |
||||||
|
final String error = |
||||||
|
messageFrame.getExceptionalHaltReasons().stream() |
||||||
|
.findFirst() |
||||||
|
.map(ExceptionalHaltReason::getDescription) |
||||||
|
.orElse( |
||||||
|
messageFrame |
||||||
|
.getRevertReason() |
||||||
|
.map(bytes -> new String(bytes.toArrayUnsafe(), StandardCharsets.UTF_8)) |
||||||
|
.orElse("")); |
||||||
|
|
||||||
|
final JsonObject results = new JsonObject(); |
||||||
|
results.put("pc", messageFrame.getPC()); |
||||||
|
results.put("op", messageFrame.getCurrentOperation().getOpcode()); |
||||||
|
results.put("gas", messageFrame.getRemainingGas().asUInt256().toShortHexString()); |
||||||
|
|
||||||
|
results.put( |
||||||
|
"gasCost", |
||||||
|
messageFrame.getCurrentOperation().cost(messageFrame).asUInt256().toShortHexString()); |
||||||
|
if (!showMemory) { |
||||||
|
results.put( |
||||||
|
"memory", |
||||||
|
messageFrame.readMemory(UInt256.ZERO, messageFrame.memoryWordSize()).toHexString()); |
||||||
|
} |
||||||
|
results.put("memSize", messageFrame.memoryByteSize()); |
||||||
|
results.put("depth", messageFrame.getMessageStackDepth() + 1); |
||||||
|
results.put("stack", stack); |
||||||
|
results.put("error", error); |
||||||
|
results.put("opName", messageFrame.getCurrentOperation().getName()); |
||||||
|
return results; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
import picocli.CommandLine.Option; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@Module |
||||||
|
public class EvmToolCommandOptionsModule { |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--revert-reason-enabled"}, |
||||||
|
paramLabel = "<Boolean>", |
||||||
|
description = "Should revert reasons be persisted. (default: ${FALLBACK-VALUE})", |
||||||
|
arity = "0..1", |
||||||
|
fallbackValue = "true") |
||||||
|
final Boolean revertReasonEnabled = true; |
||||||
|
|
||||||
|
@Provides |
||||||
|
@Named("RevertReasonEnabled") |
||||||
|
boolean isRevertReasonEnabled() { |
||||||
|
return revertReasonEnabled; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||||
|
import org.hyperledger.besu.ethereum.core.WorldUpdater; |
||||||
|
import org.hyperledger.besu.ethereum.vm.EVM; |
||||||
|
|
||||||
|
import java.util.function.Function; |
||||||
|
import javax.inject.Singleton; |
||||||
|
|
||||||
|
import dagger.Component; |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Component( |
||||||
|
modules = { |
||||||
|
ProtocolModule.class, |
||||||
|
GenesisFileModule.class, |
||||||
|
DataStoreModule.class, |
||||||
|
BlockchainModule.class, |
||||||
|
EvmToolCommandOptionsModule.class, |
||||||
|
MetricsSystemModule.class, |
||||||
|
}) |
||||||
|
public interface EvmToolComponent { |
||||||
|
|
||||||
|
Function<Integer, EVM> getEvmAtBlock(); |
||||||
|
|
||||||
|
WorldUpdater getWorldUpdater(); |
||||||
|
|
||||||
|
Blockchain getBlockchain(); |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.cli.config.EthNetworkConfig; |
||||||
|
import org.hyperledger.besu.cli.config.NetworkName; |
||||||
|
import org.hyperledger.besu.config.GenesisConfigFile; |
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.ethereum.chain.GenesisState; |
||||||
|
import org.hyperledger.besu.ethereum.core.Block; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.nio.file.Files; |
||||||
|
import javax.inject.Named; |
||||||
|
import javax.inject.Singleton; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
import io.vertx.core.json.JsonObject; |
||||||
|
|
||||||
|
@Module |
||||||
|
public class GenesisFileModule { |
||||||
|
|
||||||
|
private final String genesisConfig; |
||||||
|
|
||||||
|
protected GenesisFileModule(final File genesisFile) throws IOException { |
||||||
|
this.genesisConfig = Files.readString(genesisFile.toPath(), Charset.defaultCharset()); |
||||||
|
} |
||||||
|
|
||||||
|
protected GenesisFileModule(final String genesisConfig) { |
||||||
|
this.genesisConfig = genesisConfig; |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
GenesisConfigFile providesGenesisConfigFile() { |
||||||
|
return GenesisConfigFile.fromConfig(genesisConfig); |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
GenesisConfigOptions provideGenesisConfigOptions(final GenesisConfigFile genesisConfigFile) { |
||||||
|
return genesisConfigFile.getConfigOptions(); |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
ProtocolSchedule<?> provideProtocolSchedule( |
||||||
|
final GenesisConfigOptions configOptions, |
||||||
|
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) { |
||||||
|
throw new RuntimeException("Abstract"); |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
GenesisState provideGenesisState( |
||||||
|
final GenesisConfigFile genesisConfigFile, final ProtocolSchedule<?> protocolSchedule) { |
||||||
|
return GenesisState.fromConfig(genesisConfigFile, protocolSchedule); |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
BlockHeaderFunctions blockHashFunction() { |
||||||
|
throw new RuntimeException("Abstract"); |
||||||
|
} |
||||||
|
|
||||||
|
@Singleton |
||||||
|
@Provides |
||||||
|
@Named("GenesisBlock") |
||||||
|
Block provideGenesisBlock(final GenesisState genesisState) { |
||||||
|
return genesisState.getBlock(); |
||||||
|
} |
||||||
|
|
||||||
|
static GenesisFileModule createGenesisModule(final NetworkName networkName) { |
||||||
|
return createGenesisModule(EthNetworkConfig.jsonConfig(networkName)); |
||||||
|
} |
||||||
|
|
||||||
|
static GenesisFileModule createGenesisModule(final File genesisFile) throws IOException { |
||||||
|
return createGenesisModule(Files.readString(genesisFile.toPath(), Charset.defaultCharset())); |
||||||
|
} |
||||||
|
|
||||||
|
private static GenesisFileModule createGenesisModule(final String genesisConfig) { |
||||||
|
// duplicating work from JsonGenesisConfigOptions, but in a refactoring this goes away.
|
||||||
|
final JsonObject genesis = new JsonObject(genesisConfig); |
||||||
|
final JsonObject config = genesis.getJsonObject("config"); |
||||||
|
if (config.containsKey("ethash")) { |
||||||
|
return new MainnetGenesisFileModule(genesisConfig); |
||||||
|
} else if (config.containsKey("ibft")) { |
||||||
|
return new IBFTGenesisFileModule(genesisConfig); |
||||||
|
} else if (config.containsKey("clique")) { |
||||||
|
return new CliqueGenesisFileModule(genesisConfig); |
||||||
|
} else { |
||||||
|
// default is mainnet
|
||||||
|
return new MainnetGenesisFileModule(genesisConfig); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.consensus.ibft.IbftBlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.consensus.ibft.IbftProtocolSchedule; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
|
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
class IBFTGenesisFileModule extends GenesisFileModule { |
||||||
|
|
||||||
|
IBFTGenesisFileModule(final String genesisConfig) { |
||||||
|
super(genesisConfig); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
ProtocolSchedule<?> provideProtocolSchedule( |
||||||
|
final GenesisConfigOptions configOptions, |
||||||
|
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) { |
||||||
|
return IbftProtocolSchedule.create(configOptions); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
BlockHeaderFunctions blockHashFunction() { |
||||||
|
return IbftBlockHeaderFunctions.forOnChainBlock(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||||
|
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; |
||||||
|
|
||||||
|
class InMemoryDataStoreModule extends DataStoreModule { |
||||||
|
|
||||||
|
@Override |
||||||
|
KeyValueStorage provideKeyValueStorage() { |
||||||
|
return new InMemoryKeyValueStorage(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
|
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
class MainnetGenesisFileModule extends GenesisFileModule { |
||||||
|
|
||||||
|
MainnetGenesisFileModule(final String genesisConfig) { |
||||||
|
super(genesisConfig); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
ProtocolSchedule<?> provideProtocolSchedule( |
||||||
|
final GenesisConfigOptions configOptions, |
||||||
|
@Named("RevertReasonEnabled") final boolean revertReasonEnabled) { |
||||||
|
return MainnetProtocolSchedule.fromConfig(configOptions); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
BlockHeaderFunctions blockHashFunction() { |
||||||
|
return new MainnetBlockHeaderFunctions(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.plugin.services.MetricsSystem; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@Module |
||||||
|
public class MetricsSystemModule { |
||||||
|
|
||||||
|
@Provides |
||||||
|
MetricsSystem getMetricsSystem() { |
||||||
|
throw new RuntimeException("Abstract"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; |
||||||
|
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem; |
||||||
|
import org.hyperledger.besu.plugin.services.MetricsSystem; |
||||||
|
|
||||||
|
public class PrometheusMetricsSystemModule extends MetricsSystemModule { |
||||||
|
|
||||||
|
@Override |
||||||
|
public MetricsSystem getMetricsSystem() { |
||||||
|
return PrometheusMetricsSystem.init(MetricsConfiguration.builder().build()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
/* |
||||||
|
* 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.evmtool; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import org.hyperledger.besu.ethereum.vm.EVM; |
||||||
|
|
||||||
|
import java.util.function.Function; |
||||||
|
|
||||||
|
import dagger.Module; |
||||||
|
import dagger.Provides; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@Module(includes = GenesisFileModule.class) |
||||||
|
public class ProtocolModule { |
||||||
|
|
||||||
|
@Provides |
||||||
|
Function<Integer, EVM> provideEvmAtBlock(final ProtocolSchedule<?> protocolSchedule) { |
||||||
|
return blockNum -> protocolSchedule.getByBlockNumber(blockNum).getEvm(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue