diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 6c0423305..f0cd717e6 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -29,7 +29,7 @@ add_evmc_tool_test( add_evmc_tool_test( copy_input - "--vm $ run 600035600052596000f3 --input aabbccdd" + "--vm $ run 600035600052596000f3 --input 0xaabbccdd" "Result: +success[\r\n]+Gas used: +7[\r\n]+Output: +aabbccdd00000000000000000000000000000000000000000000000000000000[\r\n]" ) @@ -39,5 +39,47 @@ add_evmc_tool_test( "Result: +success[\r\n]+Gas used: +6[\r\n]+Output: +02[\r\n]" ) +add_test(NAME ${PROJECT_NAME}/evmc-tool/empty_code COMMAND evmc::tool --vm $ run "") +set_tests_properties(${PROJECT_NAME}/evmc-tool/empty_code PROPERTIES PASS_REGULAR_EXPRESSION "Result: +success[\r\n]+Gas used: +0[\r\n]+Output: +[\r\n]") + +add_test(NAME ${PROJECT_NAME}/evmc-tool/explicit_empty_input COMMAND evmc::tool --vm $ run 0x6000 --input "") +set_tests_properties(${PROJECT_NAME}/evmc-tool/explicit_empty_input PROPERTIES PASS_REGULAR_EXPRESSION "Result: +success[\r\n]+Gas used: +1[\r\n]+Output: +[\r\n]") + +add_evmc_tool_test( + invalid_hex_code + "--vm $ run 0x600" + "code: \\(incomplete hex byte pair\\) OR \\(File does not exist: 0x600\\)" +) + +add_evmc_tool_test( + invalid_hex_input + "--vm $ run 0x --input aa0y" + "--input: \\(invalid hex digit\\) OR \\(File does not exist: aa0y\\)" +) + +add_evmc_tool_test( + code_from_file + "--vm $ run ${CMAKE_CURRENT_SOURCE_DIR}/code.hex --input 0xaabbccdd" + "Result: +success[\r\n]+Gas used: +7[\r\n]+Output: +aabbccdd00000000000000000000000000000000000000000000000000000000[\r\n]" +) + +add_evmc_tool_test( + input_from_file + "--vm $ run 600035600052596000f3 --input ${CMAKE_CURRENT_SOURCE_DIR}/input.hex" + "Result: +success[\r\n]+Gas used: +7[\r\n]+Output: +aabbccdd00000000000000000000000000000000000000000000000000000000[\r\n]" +) + +add_evmc_tool_test( + invalid_code_file + "--vm $ run ${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm" + "Error: invalid hex digit" +) + +add_evmc_tool_test( + vm_option_fallthrough + "run --vm $ 0x600030" + "Result: +success[\r\n]+Gas used: +2[\r\n]+Output: +[\r\n]" +) + get_property(TOOLS_TESTS DIRECTORY PROPERTY TESTS) set_tests_properties(${TOOLS_TESTS} PROPERTIES ENVIRONMENT LLVM_PROFILE_FILE=${CMAKE_BINARY_DIR}/tools-%p.profraw) diff --git a/test/tools/code.hex b/test/tools/code.hex new file mode 100644 index 000000000..ea7a52c03 --- /dev/null +++ b/test/tools/code.hex @@ -0,0 +1 @@ +0x600035600052596000f3 diff --git a/test/tools/input.hex b/test/tools/input.hex new file mode 100644 index 000000000..bc1d34c70 --- /dev/null +++ b/test/tools/input.hex @@ -0,0 +1 @@ +aabbccdd diff --git a/test/tools/invalid_code.evm b/test/tools/invalid_code.evm new file mode 100644 index 000000000..b6810524c --- /dev/null +++ b/test/tools/invalid_code.evm @@ -0,0 +1 @@ +evm diff --git a/tools/evmc/main.cpp b/tools/evmc/main.cpp index da0190c6b..99caa5838 100644 --- a/tools/evmc/main.cpp +++ b/tools/evmc/main.cpp @@ -4,17 +4,52 @@ #include "tools/commands/commands.hpp" #include +#include #include +#include + +namespace +{ +/// Returns the input str if already valid hex string. Otherwise, interprets the str as a file +/// name and loads the file content. +/// @todo The file content is expected to be a hex string but not validated. +std::string load_hex(const std::string& str) +{ + const auto error_code = evmc::validate_hex(str); + if (!error_code) + return str; + + // Must be a file path. + std::ifstream file{str}; + return std::string(std::istreambuf_iterator{file}, std::istreambuf_iterator{}); +} + +struct HexValidator : public CLI::Validator +{ + HexValidator() : CLI::Validator{"HEX"} + { + name_ = "HEX"; + func_ = [](const std::string& str) -> std::string { + const auto error_code = evmc::validate_hex(str); + if (error_code) + return error_code.message(); + return {}; + }; + } +}; +} // namespace int main(int argc, const char** argv) { using namespace evmc; + static HexValidator Hex; + std::string vm_config; - std::string code_hex; + std::string code_arg; int64_t gas = 1000000; auto rev = EVMC_ISTANBUL; - std::string input_hex; + std::string input_arg; auto create = false; CLI::App app{"EVMC tool"}; @@ -22,11 +57,11 @@ int main(int argc, const char** argv) const auto& vm_option = *app.add_option("--vm", vm_config, "EVMC VM module")->envname("EVMC_VM"); - auto& run_cmd = *app.add_subcommand("run", "Execute EVM bytecode"); - run_cmd.add_option("code", code_hex, "Hex-encoded bytecode")->required(); + auto& run_cmd = *app.add_subcommand("run", "Execute EVM bytecode")->fallthrough(); + run_cmd.add_option("code", code_arg, "Bytecode")->required()->check(Hex | CLI::ExistingFile); run_cmd.add_option("--gas", gas, "Execution gas limit", true)->check(CLI::Range(0, 1000000000)); run_cmd.add_option("--rev", rev, "EVM revision", true); - run_cmd.add_option("--input", input_hex, "Hex-encoded input bytes"); + run_cmd.add_option("--input", input_arg, "Input bytes")->check(Hex | CLI::ExistingFile); run_cmd.add_flag( "--create", create, "Create new contract out of the code and then execute this contract with the input"); @@ -71,6 +106,10 @@ int main(int argc, const char** argv) throw CLI::RequiredError{vm_option.get_name()}; std::cout << "Config: " << vm_config << "\n"; + + const auto code_hex = load_hex(code_arg); + const auto input_hex = load_hex(input_arg); + // If code_hex or input_hex is not valid hex string an exception is thrown. return cmd::run(vm, rev, gas, code_hex, input_hex, create, std::cout); }