* From_json/to_json: read and write nonce and code fields as hexstrings

* Default initializer added for Account fields

* Test added checking that from_json/to_json are mutually inverse

* parseAccount() changed to be compatible with to_json/from_json logic

* * to_uint64(const string&) overload added
* Account::nonce is serialized from hex string

* to_json/from_json tests added

* added some JSON tests

* address must be lower-case, 40-bytes hex string starting with 0x

* accountFull.json test file added

* address_to_hex_string is used to serialize address field

* to_hex_string: string width should not be changed from default
This commit is contained in:
Evgeniy Shishkin 2019-08-15 11:57:58 +03:00 коммит произвёл Eddy Ashton
Родитель 5dc8a7ad9a
Коммит 881e198e8c
7 изменённых файлов: 105 добавлений и 20 удалений

6
3rdparty/test_cases/accountFull.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"address": "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"balance": "0x152d02c7e14af6800000",
"code": "0x33ff",
"nonce": "0x0"
}

Просмотреть файл

@ -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
} // namespace evm

Просмотреть файл

@ -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<uint8_t> to_bytes(const string& _s)

Просмотреть файл

@ -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(

Просмотреть файл

@ -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

Просмотреть файл

@ -19,12 +19,10 @@ using namespace nlohmann;
pair<Account, SimpleStorage> parseAccount(json::const_iterator& it)
{
auto j = it.value();
return {{from_hex_str(it.key()),
to_uint64(j["nonce"]),
j["balance"].get<uint256_t>(),
to_bytes(j["code"])},
j["storage"]};
auto storage = it.value()["storage"];
auto account = it.value().get<Account>();
account.address = from_hex_str(it.key());
return {account, storage};
}
void run_test_case(

Просмотреть файл

@ -10,10 +10,66 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
#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<Account>();
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<Account>();
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<Account>();
nlohmann::json j2 = a1;
REQUIRE(j == j2);
}
}
TEST_CASE("util" * doctest::test_suite("util"))
{
SUBCASE("to_bytes")