diff --git a/3rdparty/test_cases/accountFull.json b/3rdparty/test_cases/accountFull.json new file mode 100644 index 0000000..f65f0ec --- /dev/null +++ b/3rdparty/test_cases/accountFull.json @@ -0,0 +1,6 @@ +{ + "address": "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "balance": "0x152d02c7e14af6800000", + "code": "0x33ff", + "nonce": "0x0" +} diff --git a/evm/account.cpp b/evm/account.cpp index 3352a59..61c83f3 100644 --- a/evm/account.cpp +++ b/evm/account.cpp @@ -55,17 +55,21 @@ namespace evm void to_json(nlohmann::json& j, const Account& a) { - j["address"] = a.address; + j["address"] = address_to_hex_string(a.address); j["balance"] = a.balance; - j["nonce"] = a.nonce; - j["code"] = a.code; + j["nonce"] = to_hex_string(a.nonce); + j["code"] = to_hex_string(a.code); } void from_json(const nlohmann::json& j, Account& a) { - assign_j(a.address, j["address"]); - assign_j(a.balance, j["balance"]); - assign_j(a.nonce, j["nonce"]); - assign_j(a.code, j["code"]); + if (j.find("address") != j.end()) + assign_j(a.address, j["address"]); + if (j.find("balance") != j.end()) + assign_j(a.balance, j["balance"]); + if (j.find("nonce") != j.end()) + assign_j(a.nonce, to_uint64(j["nonce"])); + if (j.find("code") != j.end()) + assign_j(a.code, to_bytes(j["code"])); } -} // namespace evm \ No newline at end of file +} // namespace evm diff --git a/evm/util.cpp b/evm/util.cpp index 18ed29c..14c9bb9 100644 --- a/evm/util.cpp +++ b/evm/util.cpp @@ -16,10 +16,14 @@ namespace evm return (s.size() >= 2 && s[1] == 'x') ? s.substr(2) : s; } + uint64_t to_uint64(const std::string& s) + { + return strtoull(&s[0], nullptr, 16); + } + uint64_t to_uint64(const nlohmann::json& j) { - string s = j; - return strtoull(&s[0], nullptr, 16); + return to_uint64(string(j)); } vector to_bytes(const string& _s) diff --git a/include/account.h b/include/account.h index 6b937fa..64e3007 100644 --- a/include/account.h +++ b/include/account.h @@ -15,10 +15,10 @@ namespace evm struct Account { - Address address; - uint64_t nonce; - uint256_t balance; - Code code; + Address address = {}; + uint64_t nonce = {}; + uint256_t balance = {}; + Code code = {}; Account() = default; Account( diff --git a/include/util.h b/include/util.h index 21c6ca5..0261ed1 100644 --- a/include/util.h +++ b/include/util.h @@ -111,6 +111,22 @@ namespace evm return to_hex_string(bytes.begin(), bytes.end()); } + inline std::string to_hex_string(uint64_t v) + { + std::stringstream ss; + ss << "0x" << std::hex << v; + return ss.str(); + } + + inline auto address_to_hex_string(const Address& v) + { + std::stringstream ss; + ss << "0x" << std::hex << std::setw(40) << std::setfill('0') << v; + auto s = ss.str(); + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; + } + inline std::string to_checksum_address(const Address& a) { auto s = to_lower_hex_str(a); @@ -142,4 +158,5 @@ namespace evm Address generate_address(const Address& sender, uint64_t nonce); uint64_t to_uint64(const nlohmann::json& j); + uint64_t to_uint64(const std::string& s); } // namespace evm diff --git a/tests/harness.cpp b/tests/harness.cpp index 0a5a179..cb26d73 100644 --- a/tests/harness.cpp +++ b/tests/harness.cpp @@ -19,12 +19,10 @@ using namespace nlohmann; pair parseAccount(json::const_iterator& it) { - auto j = it.value(); - return {{from_hex_str(it.key()), - to_uint64(j["nonce"]), - j["balance"].get(), - to_bytes(j["code"])}, - j["storage"]}; + auto storage = it.value()["storage"]; + auto account = it.value().get(); + account.address = from_hex_str(it.key()); + return {account, storage}; } void run_test_case( diff --git a/tests/main.cpp b/tests/main.cpp index d37c16f..261ce08 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -10,10 +10,66 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include +#include +#include +#include + +#include "../include/bigint.h" using namespace std; using namespace evm; +TEST_CASE("from_json/to_json are mutually inverse") +{ + constexpr auto env_var = "TEST_DIR"; + auto test_dir = getenv(env_var); + if (!test_dir) { + throw std::logic_error( + "Must set path to test cases in " + std::string(env_var) + + " environment variable"); + } + SUBCASE("Using default Account objects") + { + Account a1; + nlohmann::json j = a1; + Account a2 = j.get(); + REQUIRE(a1 == a2); + } + SUBCASE("Using non-default values for Account") + { + Account a1; + a1.address = from_hex_str("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); + a1.nonce = to_uint64(string("0x66")); + nlohmann::json j = a1; + Account a2 = j; + REQUIRE(a1 == a2); + } + SUBCASE("Using partially defined JSON as a source for Account") + { + auto test_path = string(test_dir) + "/vmTests.json"; + const auto j = nlohmann::json::parse(std::ifstream(test_path)); + const auto rec = *j["suicide"]["pre"].begin(); + Account a1 = rec.get(); + nlohmann::json j2 = a1; + if (rec.find("balance") != rec.end()) + CHECK(a1.balance == from_hex_str(j2["balance"])); + if (rec.find("code") != rec.end()) + CHECK(a1.code == to_bytes(j2["code"])); + if (rec.find("nonce") != rec.end()) + CHECK(a1.nonce == to_uint64(j2["nonce"])); + if (rec.find("address") != rec.end()) + CHECK(a1.address == from_hex_str(j2["address"])); + } + SUBCASE("Using fully defined JSON as a source for Account") + { + auto test_path = string(test_dir) + "/accountFull.json"; + const auto j = nlohmann::json::parse(std::ifstream(test_path)); + Account a1 = j.get(); + nlohmann::json j2 = a1; + REQUIRE(j == j2); + } +} + TEST_CASE("util" * doctest::test_suite("util")) { SUBCASE("to_bytes")