From 7de6e6edb3f3644224427e7596d46fc51dc2b082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 23 May 2022 13:49:14 +0200 Subject: [PATCH 1/2] tool: Use @file syntax to loading data from a file For --input and code arguments allow loading data from a file by preceding the file path with @ character. E.g. `--input @sha1.in`. --- test/tools/CMakeLists.txt | 10 +++++----- tools/evmc/main.cpp | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 6f4fa46ae..6ff6e44cf 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -54,30 +54,30 @@ set_tests_properties(${PROJECT_NAME}/evmc-tool/explicit_empty_input PROPERTIES P add_evmc_tool_test( invalid_hex_code "--vm $ run 0x600" - "code: \\(invalid hex\\) OR \\(File does not exist: 0x600\\)" + "code: invalid hex" ) add_evmc_tool_test( invalid_hex_input "--vm $ run 0x --input aa0y" - "--input: \\(invalid hex\\) OR \\(File does not exist: aa0y\\)" + "--input: invalid hex" ) add_evmc_tool_test( code_from_file - "--vm $ run ${CMAKE_CURRENT_SOURCE_DIR}/code.hex --input 0xaabbccdd" + "--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" + "--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" + "--vm $ run @${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm" "Error: invalid hex" ) diff --git a/tools/evmc/main.cpp b/tools/evmc/main.cpp index 72f02ab96..2ef7baea1 100644 --- a/tools/evmc/main.cpp +++ b/tools/evmc/main.cpp @@ -10,25 +10,27 @@ namespace { -/// Returns the input str if already valid hex string. Otherwise, interprets the str as a file -/// name and loads the file content. +/// If the argument starts with @ returns the content of the file at the path following @. +/// Otherwise, returns the argument. /// @todo The file content is expected to be a hex string but not validated. std::string load_hex(const std::string& str) { - if (evmc::validate_hex(str)) - return str; + if (str[0] == '@') // The argument is file path. + { + std::ifstream file{str.c_str() + 1}; + return {std::istreambuf_iterator{file}, std::istreambuf_iterator{}}; + } - // Must be a file path. - std::ifstream file{str}; - return {std::istreambuf_iterator{file}, std::istreambuf_iterator{}}; + return str; } -struct HexValidator : public CLI::Validator +struct HexOrFileValidator : public CLI::Validator { - HexValidator() : CLI::Validator{"HEX"} + HexOrFileValidator() : CLI::Validator{"HEX|@FILE"} { - name_ = "HEX"; func_ = [](const std::string& str) -> std::string { + if (!str.empty() && str[0] == '@') + return CLI::ExistingFile(str.substr(1)); if (!evmc::validate_hex(str)) return "invalid hex"; return {}; @@ -43,7 +45,7 @@ int main(int argc, const char** argv) noexcept try { - HexValidator Hex; + HexOrFileValidator HexOrFile; std::string vm_config; std::string code_arg; @@ -59,13 +61,11 @@ int main(int argc, const char** argv) noexcept *app.add_option("--vm", vm_config, "EVMC VM module")->envname("EVMC_VM"); 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("code", code_arg, "Bytecode")->required()->check(HexOrFile); 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_arg, "Input bytes")->check(Hex | CLI::ExistingFile); + run_cmd.add_option("--input", input_arg, "Input bytes")->check(HexOrFile); run_cmd.add_flag( "--create", create, "Create new contract out of the code and then execute this contract with the input"); From f5cf3ab0b0342fb86ae3bd952bf7529393ba0331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 1 Jun 2022 13:38:10 +0200 Subject: [PATCH 2/2] tool: Refactor run() to take bytes not hex Change the type of code and input parameters of tooling::run() from hex-encoded string to bytes. The hex decoding must happen outside. This moves the hex decoding errors out of the function concern. --- include/evmc/tooling.hpp | 6 +++--- lib/tooling/run.cpp | 14 +++---------- test/tools/CMakeLists.txt | 2 +- test/unittests/tooling_test.cpp | 37 ++++++++++++++++++++------------- tools/evmc/main.cpp | 26 ++++++++++++++--------- 5 files changed, 45 insertions(+), 40 deletions(-) diff --git a/include/evmc/tooling.hpp b/include/evmc/tooling.hpp index de7d14b86..3177ec20a 100644 --- a/include/evmc/tooling.hpp +++ b/include/evmc/tooling.hpp @@ -8,11 +8,11 @@ namespace evmc::tooling { -int run(evmc::VM& vm, +int run(VM& vm, evmc_revision rev, int64_t gas, - const std::string& code_hex, - const std::string& input_hex, + bytes_view code, + bytes_view input, bool create, bool bench, std::ostream& out); diff --git a/lib/tooling/run.cpp b/lib/tooling/run.cpp index 7dac8edc7..dff8fc659 100644 --- a/lib/tooling/run.cpp +++ b/lib/tooling/run.cpp @@ -59,11 +59,11 @@ auto bench(MockedHost& host, } } // namespace -int run(evmc::VM& vm, +int run(VM& vm, evmc_revision rev, int64_t gas, - const std::string& code_hex, - const std::string& input_hex, + bytes_view code, + bytes_view input, bool create, bool bench, std::ostream& out) @@ -71,14 +71,6 @@ int run(evmc::VM& vm, out << (create ? "Creating and executing on " : "Executing on ") << rev << " with " << gas << " gas limit\n"; - auto opt_code = from_hex(code_hex); - auto opt_input = from_hex(input_hex); - if (!opt_code || !opt_input) - throw std::invalid_argument{"invalid hex"}; - - const auto& code = *opt_code; - const auto& input = *opt_input; - MockedHost host; evmc_message msg{}; diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 6ff6e44cf..86d8d32ef 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -78,7 +78,7 @@ add_evmc_tool_test( add_evmc_tool_test( invalid_code_file "--vm $ run @${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm" - "Error: invalid hex" + "Error: invalid hex in ${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm" ) add_evmc_tool_test( diff --git a/test/unittests/tooling_test.cpp b/test/unittests/tooling_test.cpp index 14ed698a9..f7c41fc63 100644 --- a/test/unittests/tooling_test.cpp +++ b/test/unittests/tooling_test.cpp @@ -3,11 +3,13 @@ // Licensed under the Apache License, Version 2.0. #include "examples/example_vm/example_vm.h" +#include #include #include #include using namespace evmc::tooling; +using evmc::from_hex; namespace { @@ -33,7 +35,7 @@ TEST(tool_commands, run_empty_code) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_FRONTIER, 1, "", "", false, false, out); + const auto exit_code = run(vm, EVMC_FRONTIER, 1, {}, {}, false, false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Frontier", 1, "success", 0, "")); } @@ -43,7 +45,8 @@ TEST(tool_commands, run_oog) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_BERLIN, 2, "0x6002600201", "", false, false, out); + const auto exit_code = + run(vm, EVMC_BERLIN, 2, *from_hex("0x6002600201"), {}, false, false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Berlin", 2, "out of gas", 2)); } @@ -53,7 +56,8 @@ TEST(tool_commands, run_return_my_address) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_HOMESTEAD, 200, "30600052596000f3", "", false, false, out); + const auto exit_code = + run(vm, EVMC_HOMESTEAD, 200, *from_hex("30600052596000f3"), {}, false, false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Homestead", 200, "success", 6, @@ -67,8 +71,9 @@ TEST(tool_commands, run_copy_input_to_output) std::ostringstream out; const auto exit_code = - run(vm, EVMC_TANGERINE_WHISTLE, 200, "600035600052596000f3", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", false, false, out); + run(vm, EVMC_TANGERINE_WHISTLE, 200, *from_hex("600035600052596000f3"), + *from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), false, + false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Tangerine Whistle", 200, "success", 7, @@ -82,8 +87,9 @@ TEST(tool_commands, create_return_1) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_SPURIOUS_DRAGON, 200, - "6960016000526001601ff3600052600a6016f3", "", true, false, out); + const auto exit_code = + run(vm, EVMC_SPURIOUS_DRAGON, 200, *from_hex("6960016000526001601ff3600052600a6016f3"), {}, + true, false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Spurious Dragon", 200, "success", 6, "01", true)); } @@ -96,8 +102,8 @@ TEST(tool_commands, create_copy_input_to_output) std::ostringstream out; const auto exit_code = - run(vm, EVMC_SPURIOUS_DRAGON, 200, "69600035600052596000f3600052600a6016f3", "0c49c4", true, - false, out); + run(vm, EVMC_SPURIOUS_DRAGON, 200, *from_hex("69600035600052596000f3600052600a6016f3"), + *from_hex("0c49c4"), true, false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ( out.str(), @@ -112,7 +118,7 @@ TEST(tool_commands, create_failure_stack_underflow) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_PETERSBURG, 0, "fe", "", true, false, out); + const auto exit_code = run(vm, EVMC_PETERSBURG, 0, *from_hex("fe"), {}, true, false, out); EXPECT_EQ(exit_code, EVMC_UNDEFINED_INSTRUCTION); EXPECT_EQ(out.str(), "Creating and executing on Petersburg with 0 gas limit\n" @@ -127,8 +133,9 @@ TEST(tool_commands, create_preserve_storage) std::ostringstream out; const auto exit_code = - run(vm, EVMC_BERLIN, 200, "60bb 6000 55 6a6000546000526001601ff3 6000 52 600b 6015 f3", "", - true, false, out); + run(vm, EVMC_BERLIN, 200, + *from_hex("60bb 6000 55 6a6000546000526001601ff3 6000 52 600b 6015 f3"), {}, true, + false, out); EXPECT_EQ(exit_code, 0); EXPECT_EQ(out.str(), out_pattern("Berlin", 200, "success", 7, "bb", true)); } @@ -138,7 +145,7 @@ TEST(tool_commands, bench_add) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto exit_code = run(vm, EVMC_LONDON, 200, "6002 80 01", "", false, true, out); + const auto exit_code = run(vm, EVMC_LONDON, 200, *from_hex("6002 80 01"), {}, false, true, out); EXPECT_EQ(exit_code, 0); const auto o = out.str(); @@ -153,8 +160,8 @@ TEST(tool_commands, bench_inconsistent_output) auto vm = evmc::VM{evmc_create_example_vm()}; std::ostringstream out; - const auto code = "6000 54 6001 6000 55 6000 52 6001 601f f3"; - const auto exit_code = run(vm, EVMC_BYZANTIUM, 200, code, "", false, true, out); + const auto code = *from_hex("6000 54 6001 6000 55 6000 52 6001 601f f3"); + const auto exit_code = run(vm, EVMC_BYZANTIUM, 200, code, {}, false, true, out); EXPECT_EQ(exit_code, 0); const auto o = out.str(); diff --git a/tools/evmc/main.cpp b/tools/evmc/main.cpp index 2ef7baea1..f64a2a8e9 100644 --- a/tools/evmc/main.cpp +++ b/tools/evmc/main.cpp @@ -10,18 +10,24 @@ namespace { -/// If the argument starts with @ returns the content of the file at the path following @. -/// Otherwise, returns the argument. +/// If the argument starts with @ returns the hex-decoded contents of the file +/// at the path following the @. Otherwise, returns the argument. /// @todo The file content is expected to be a hex string but not validated. -std::string load_hex(const std::string& str) +evmc::bytes load_from_hex(const std::string& str) { if (str[0] == '@') // The argument is file path. { - std::ifstream file{str.c_str() + 1}; - return {std::istreambuf_iterator{file}, std::istreambuf_iterator{}}; + const auto path = str.substr(1); + std::ifstream file{path}; + const std::string content{std::istreambuf_iterator{file}, + std::istreambuf_iterator{}}; + auto o = evmc::from_hex(content); + if (!o) + throw std::invalid_argument{"invalid hex in " + path}; + return std::move(*o); } - return str; + return evmc::from_hex(str).value(); // Should be validated already. } struct HexOrFileValidator : public CLI::Validator @@ -114,10 +120,10 @@ int main(int argc, const char** argv) noexcept 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 tooling::run(vm, rev, gas, code_hex, input_hex, create, bench, std::cout); + // If code_arg or input_arg contains invalid hex string an exception is thrown. + const auto code = load_from_hex(code_arg); + const auto input = load_from_hex(input_arg); + return tooling::run(vm, rev, gas, code, input, create, bench, std::cout); } return 0;