Skip to content

Commit

Permalink
Merge pull request #647 from ethereum/cli_file
Browse files Browse the repository at this point in the history
tool: Use @file syntax to loading data from a file
  • Loading branch information
chfast authored Jun 1, 2022
2 parents 2e25a9a + f5cf3ab commit 9ab1fe4
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 55 deletions.
6 changes: 3 additions & 3 deletions include/evmc/tooling.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 3 additions & 11 deletions lib/tooling/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,18 @@ 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)
{
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{};
Expand Down
12 changes: 6 additions & 6 deletions test/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,31 @@ set_tests_properties(${PROJECT_NAME}/evmc-tool/explicit_empty_input PROPERTIES P
add_evmc_tool_test(
invalid_hex_code
"--vm $<TARGET_FILE:evmc::example-vm> run 0x600"
"code: \\(invalid hex\\) OR \\(File does not exist: 0x600\\)"
"code: invalid hex"
)

add_evmc_tool_test(
invalid_hex_input
"--vm $<TARGET_FILE:evmc::example-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 $<TARGET_FILE:evmc::example-vm> run ${CMAKE_CURRENT_SOURCE_DIR}/code.hex --input 0xaabbccdd"
"--vm $<TARGET_FILE:evmc::example-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 $<TARGET_FILE:evmc::example-vm> run 600035600052596000f3 --input ${CMAKE_CURRENT_SOURCE_DIR}/input.hex"
"--vm $<TARGET_FILE:evmc::example-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 $<TARGET_FILE:evmc::example-vm> run ${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm"
"Error: invalid hex"
"--vm $<TARGET_FILE:evmc::example-vm> run @${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm"
"Error: invalid hex in ${CMAKE_CURRENT_SOURCE_DIR}/invalid_code.evm"
)

add_evmc_tool_test(
Expand Down
37 changes: 22 additions & 15 deletions test/unittests/tooling_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// Licensed under the Apache License, Version 2.0.

#include "examples/example_vm/example_vm.h"
#include <evmc/hex.hpp>
#include <evmc/tooling.hpp>
#include <gtest/gtest.h>
#include <sstream>

using namespace evmc::tooling;
using evmc::from_hex;

namespace
{
Expand All @@ -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, ""));
}
Expand All @@ -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));
}
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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));
}
Expand All @@ -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(),
Expand All @@ -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"
Expand All @@ -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));
}
Expand All @@ -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();
Expand All @@ -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();
Expand Down
46 changes: 26 additions & 20 deletions tools/evmc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,33 @@

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 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 (evmc::validate_hex(str))
return str;
if (str[0] == '@') // The argument is file path.
{
const auto path = str.substr(1);
std::ifstream file{path};
const std::string content{std::istreambuf_iterator<char>{file},
std::istreambuf_iterator<char>{}};
auto o = evmc::from_hex(content);
if (!o)
throw std::invalid_argument{"invalid hex in " + path};
return std::move(*o);
}

// Must be a file path.
std::ifstream file{str};
return {std::istreambuf_iterator<char>{file}, std::istreambuf_iterator<char>{}};
return evmc::from_hex(str).value(); // Should be validated already.
}

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 {};
Expand All @@ -43,7 +51,7 @@ int main(int argc, const char** argv) noexcept

try
{
HexValidator Hex;
HexOrFileValidator HexOrFile;

std::string vm_config;
std::string code_arg;
Expand All @@ -59,13 +67,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");
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 9ab1fe4

Please sign in to comment.