eEVM/tests/harness.cpp

349 строки
9.1 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "eEVM/disassembler.h"
#include "eEVM/opcode.h"
#include "eEVM/processor.h"
#include "eEVM/rlp.h"
#include "eEVM/simple/simpleglobalstate.h"
#include "eEVM/simple/simplestorage.h"
#include "eEVM/util.h"
#include <doctest/doctest.h>
#include <fmt/format_header_only.h>
#include <fstream>
#include <nlohmann/json.hpp>
using namespace std;
using namespace eevm;
using namespace nlohmann;
SimpleGlobalState::StateEntry parseAccount(json::const_iterator& it)
{
auto storage = it.value()["storage"];
auto account = it.value().get<SimpleAccount>();
account.set_address(to_uint256(it.key()));
return {account, storage};
}
void run_test_case(
const string& fileName,
const set<string>& skip,
const bool checkLogs,
const bool disasm = true)
{
constexpr auto env_var = "TEST_DIR";
auto test_dir = getenv(env_var);
string testPath = fileName;
if (test_dir)
{
testPath = fmt::format("{}/{}", string(test_dir), fileName);
}
else
{
throw std::logic_error(fmt::format(
"Must set path to test cases in {} environment variable", env_var));
}
#ifdef RECORD_TRACE
const string delim(15, '-');
cout << fmt::format("Test file {}\n{}", testPath, delim) << endl;
#endif
ifstream fs(testPath);
{
INFO("Looking for " << testPath);
REQUIRE(fs.good());
}
const auto cases = json::parse(fs);
int i = 0;
for (decltype(auto) it = cases.cbegin(); it != cases.cend(); it++, i++)
{
if (skip.find(it.key()) != skip.end())
continue;
auto subcase = it.key();
INFO("SUBCASE: " << subcase);
{
const auto& j = it.value();
Address callee = to_uint256(j["exec"]["address"]);
Address caller = to_uint256(j["exec"]["caller"]);
const auto c = to_bytes(j["exec"]["code"]);
const auto inp = to_bytes(j["exec"]["data"]);
uint64_t value = to_uint64(j["exec"]["value"]);
#ifdef RECORD_TRACE
cout << fmt::format("Case {} (#{})", it.key(), i) << endl;
if (disasm)
{
try
{
cout << "Disassembly:" << endl << Disassembler::dis(c) << endl;
}
catch (const exception& e)
{
cerr << fmt::format("Failed to disassemble: {}", e.what()) << endl;
continue;
}
cout << delim << endl;
}
#endif
Block b = j["env"];
SimpleGlobalState gs(b);
NullLogHandler ignore;
Transaction tx(
to_uint256(j["exec"]["origin"]),
ignore,
to_uint64(j["exec"]["value"]),
to_uint64(j["exec"]["gasPrice"]),
to_uint64(j["exec"]["gas"]));
// parse accounts
for (decltype(auto) it = j["pre"].cbegin(); it != j["pre"].cend(); it++)
{
gs.insert(parseAccount(it));
}
CHECK(gs.exists(callee));
Processor p(gs);
Trace* tr(nullptr);
#ifdef RECORD_TRACE
tr = new Trace();
#endif
const auto e = p.run(tx, caller, gs.get(callee), inp, value, tr);
#ifdef RECORD_TRACE
cout << fmt::format("Trace:\n{}{}", *tr, delim) << endl;
delete tr;
#endif
// was the run supposed to fail?
const auto post = j.find("post");
if (post == j.end())
{
CHECK((e.er == ExitReason::threw || e.er == ExitReason::halted));
continue;
}
uint64_t postAccounts = 0;
for (auto it = post.value().cbegin(); it != post.value().cend(); it++)
{
const auto expected = parseAccount(it);
const auto actual = gs.get(expected.first.get_address());
CHECK(expected.first == actual.acc);
decltype(auto) st = dynamic_cast<SimpleStorage&>(actual.st);
CHECK(st == expected.second);
}
// the != 0 check exists because there exists failure scenarios where post
// is not mentioned when an exception is raised but that does not mean
// systemState is lost
if (postAccounts != 0)
{
CHECK(postAccounts == gs.num_accounts());
}
// does the test case specify output?
if (j.find("out") != j.end())
{
CHECK(e.output == to_bytes(j.at("out")));
}
// TODO: Calculate hash of RLP of produced log entries, to compare against
// the value specified in the test case
}
}
}
TEST_CASE("vmBitwiseLogicOperationTest" * doctest::test_suite("logic"))
{
run_test_case("vmBitwiseLogicOperationTest.json", {}, true);
}
TEST_CASE("vmEnvironmentalInfoTest" * doctest::test_suite("logic"))
{
auto skip = set<string>{
// memory index > 2^64
"calldatacopy_DataIndexTooHigh",
"calldatacopy_DataIndexTooHigh2",
"calldatacopy_DataIndexTooHigh2_return",
"calldatacopy_DataIndexTooHigh_return",
"calldataloadSizeTooHigh",
"calldataload_BigOffset",
"codecopy_DataIndexTooHigh",
};
run_test_case("vmEnvironmentalInfoTest.json", skip, true);
}
TEST_CASE("vmTests" * doctest::test_suite("logic"))
{
auto skip = set<string>{};
run_test_case("vmTests.json", skip, true);
}
// These pass, but are kept hidden by default because they're slow
TEST_CASE(
"vmPerformanceTest" * doctest::test_suite(".performance") * doctest::skip())
{
auto skip = set<string>{// Missing post
"ackermann33",
// Empty output
"loop-exp-16b-100k",
"loop-exp-32b-100k",
// Incorrect output
"loop-exp-8b-100k"};
run_test_case("vmPerformanceTest.json", skip, false);
}
TEST_CASE("vmSystemOperationsTest" * doctest::test_suite("logic"))
{
auto skip = set<string>{};
run_test_case("vmSystemOperationsTest.json", skip, false);
}
// Passes, but slow, so disabled by default
TEST_CASE(
"vmInputLimitsLight" * doctest::test_suite(".input") * doctest::skip())
{
auto skip = set<string>{
// copies code to offset > 2^64
"012fd315e355bad0d1bdce9a44863f3c909bfdf9909779c431c9e0fdf9ae339f",
"01923ee9def56e347452847fd9be4577f8b663097620664ba24317f67a73122a",
"01a5cf9db140969b2a2410361164fc41c64c070805b82116d217240d4e304f6f",
"01d740c2964a008fc6998e2d0cf2df984c8451369737426ad5640a129be6c5dd",
// logs data with size > 2^64
"01854150aba4ddc54c4ac0a61e21b838cb53017d0fa83faf8e146233337cb1fb",
"0322751b60db071ea7c6885f6f3eaf0b83af83856ba5a72e3a87404cc171fac3"};
run_test_case("vmInputLimitsLight.json", skip, true);
}
TEST_CASE("vmArithmeticTest" * doctest::test_suite("logic"))
{
auto skip = set<string>{
// exponent wider than 64 bits
"exp1",
// exponents too large ~(256 ^ (256 ^ n))
"expPowerOf256Of256_4",
"expPowerOf256Of256_5",
"expPowerOf256Of256_6",
"expPowerOf256Of256_7",
"expPowerOf256Of256_8",
"expPowerOf256Of256_9",
"expPowerOf256Of256_10",
"expPowerOf256Of256_11",
"expPowerOf256Of256_12",
"expPowerOf256Of256_13",
"expPowerOf256Of256_14",
"expPowerOf256Of256_15",
"expPowerOf256Of256_16",
"expPowerOf256Of256_17",
"expPowerOf256Of256_18",
"expPowerOf256Of256_19",
"expPowerOf256Of256_20",
"expPowerOf256Of256_21",
"expPowerOf256Of256_22",
"expPowerOf256Of256_23",
"expPowerOf256Of256_24",
"expPowerOf256Of256_25",
"expPowerOf256Of256_26",
"expPowerOf256Of256_27",
"expPowerOf256Of256_28",
"expPowerOf256Of256_29",
"expPowerOf256Of256_30",
"expPowerOf256Of256_31",
"expPowerOf256Of256_32",
"expPowerOf256Of256_33",
// filler and code say store[2] = result, but it's not in the expected post
"expXY"};
run_test_case("vmArithmeticTest.json", skip, true);
}
// Passes, but not testing anything interesting because we're currently ignoring
// the logs
TEST_CASE("vmLogTest" * doctest::test_suite("env"))
{
auto skip = set<string>{
// mem access > 2^64
"log0_logMemStartTooHigh",
"log0_logMemsizeTooHigh",
"log1_logMemStartTooHigh",
"log1_logMemsizeTooHigh",
"log2_logMemStartTooHigh",
"log2_logMemsizeTooHigh",
"log3_logMemStartTooHigh",
"log3_logMemsizeTooHigh",
"log4_logMemStartTooHigh",
"log4_logMemsizeTooHigh",
};
run_test_case("vmLogTest.json", skip, true);
}
TEST_CASE("vmPushDupSwapTest" * doctest::test_suite("logic"))
{
auto skip = set<string>{
// clearly valid program, but no post defined
"push33",
};
run_test_case("vmPushDupSwapTest.json", skip, true);
}
TEST_CASE("vmIOandFlowOperationsTest" * doctest::test_suite("env"))
{
auto skip = set<string>{
// infinite loop, expects to run out of gas
"BlockNumberDynamicJump0_foreverOutOfGas",
"DynamicJump0_foreverOutOfGas",
"JDfromStorageDynamicJump0_foreverOutOfGas",
"jump0_foreverOutOfGas",
// missing gas
"gas0",
"gas1",
// seem like correct program, why no post here?
"return1",
};
run_test_case("vmIOandFlowOperationsTest.json", skip, true);
}
TEST_CASE("vmBlockInfoTest" * doctest::test_suite("env"))
{
auto skip = set<string>{"gaslimit"};
run_test_case("vmBlockInfoTest.json", skip, true);
}
TEST_CASE("vmRandomTest" * doctest::test_suite("rand"))
{
auto skip = set<string>{};
run_test_case("vmRandomTest.json", skip, true);
}
TEST_CASE("vmSha3Test" * doctest::test_suite("sha"))
{
auto skip = set<string>{};
run_test_case("vmSha3Test.json", skip, true);
}