* Initial implementation of abstract Account

* Update samples

* Simplify API

* Remove incorrect comment

* Simplify further - common implementation of pay_to

* Add libfmt license notice

* Merge fixups

* Disable automatic nonce-increment pending further investigation

* Allow nonce to be set in constructor

* Remove unnecessary methods from abstract interfaces
This commit is contained in:
Eddy Ashton 2019-08-23 13:18:25 +01:00 коммит произвёл GitHub
Родитель 5f73998ac0
Коммит 36ca7e32ea
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 314 добавлений и 198 удалений

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

@ -45,7 +45,6 @@ set(EEVM_INCLUDE_DIRS
)
set(EEVM_CORE_SRCS
src/account.cpp
src/disassembler.cpp
src/processor.cpp
src/stack.cpp
@ -54,6 +53,7 @@ set(EEVM_CORE_SRCS
)
set(EEVM_SIMPLE_SRCS
src/simple/simpleaccount.cpp
src/simple/simpleglobalstate.cpp
src/simple/simplestorage.cpp
)

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

@ -2,56 +2,66 @@
// Licensed under the MIT License.
#pragma once
#include "address.h"
#include "bigint.h"
#include "storage.h"
#include "util.h"
#include <nlohmann/json.hpp>
#include "exception.h"
namespace eevm
{
using Code = std::vector<uint8_t>;
/**
* Abstract interface for interacting with EVM accounts
*/
struct Account
{
Address address = {};
uint64_t nonce = {};
uint256_t balance = {};
Code code = {};
using Nonce = size_t;
Account() = default;
Account(
const Address& address, const uint256_t& balance, const Code& code) :
address(address),
nonce(0),
balance(balance),
code(code)
{}
virtual ~Account() {}
Account(
const Address& address,
uint64_t nonce,
const uint256_t& balance,
const Code& code) :
address(address),
nonce(nonce),
balance(balance),
code(code)
{}
virtual Address get_address() const = 0;
bool has_code() const;
void set_code(Code&& code_);
virtual uint256_t get_balance() const = 0;
virtual void set_balance(const uint256_t& b) = 0;
void pay(const uint256_t& amount);
void pay(Account& r, const uint256_t& amount);
virtual void pay_to(Account& other, const uint256_t& amount)
{
const auto this_balance = get_balance();
if (amount > this_balance)
{
throw Exception(
Exception::Type::outOfFunds,
"Insufficient funds to pay " + to_hex_str(amount) + " to " +
to_hex_str(other.get_address()) + " (from " +
to_hex_str(get_address()) + ", current balance " +
to_hex_str(this_balance) + ")");
}
bool operator==(const Account& that) const;
const auto other_balance = other.get_balance();
const auto proposed_balance = other_balance + amount;
if (proposed_balance < other_balance)
{
throw Exception(
Exception::Type::overflow,
"Overflow while attempting to pay " + to_hex_str(amount) + " to " +
to_hex_str(other.get_address()) + " (current balance " +
to_hex_str(other_balance) + ") from " + to_hex_str(get_address()));
}
friend void to_json(nlohmann::json&, const Account&);
friend void from_json(const nlohmann::json&, Account&);
set_balance(this_balance - amount);
other.set_balance(proposed_balance);
}
virtual Nonce get_nonce() const = 0;
virtual void increment_nonce() = 0;
virtual Code get_code() const = 0;
virtual void set_code(Code&& code) = 0;
virtual bool has_code()
{
return !get_code().empty();
}
};
void to_json(nlohmann::json&, const Account&);
void from_json(const nlohmann::json&, Account&);
} // namespace eevm
} // namespace evm

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

@ -17,6 +17,7 @@ namespace eevm
outOfBounds,
outOfGas,
outOfFunds,
overflow,
illegalInstruction,
notImplemented
};

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

@ -21,8 +21,10 @@ namespace eevm
template <
typename T,
typename = std::enable_if_t<std::is_base_of<Storage, T>::value>>
AccountState(std::pair<Account, T>& p) : acc(p.first), st(p.second)
typename U,
typename = std::enable_if_t<std::is_base_of<Account, T>::value>,
typename = std::enable_if_t<std::is_base_of<Storage, U>::value>>
AccountState(std::pair<T, U>& p) : acc(p.first), st(p.second)
{}
AccountState(Account& acc, Storage& st) : acc(acc), st(st) {}
};
@ -32,7 +34,6 @@ namespace eevm
*/
struct GlobalState
{
virtual bool exists(const Address& addr) = 0;
virtual void remove(const Address& addr) = 0;
/**
@ -43,8 +44,6 @@ namespace eevm
virtual AccountState create(
const Address& addr, const uint256_t& balance, const Code& code) = 0;
virtual size_t num_accounts() = 0;
virtual const Block& get_current_block() = 0;
virtual uint256_t get_block_hash(uint8_t offset) = 0;
};

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

@ -22,10 +22,10 @@ namespace eevm
struct ExecResult
{
ExitReason er;
Exception::Type ex;
std::string exmsg;
std::vector<uint8_t> output;
ExitReason er = {};
Exception::Type ex = {};
std::string exmsg = {};
std::vector<uint8_t> output = {};
};
/**

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

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "eEVM/account.h"
#include <nlohmann/json.hpp>
namespace eevm
{
/**
* Simple implementation of Account
*/
class SimpleAccount : public Account
{
private:
Address address = {};
uint256_t balance = {};
Code code = {};
Nonce nonce = {};
public:
SimpleAccount() = default;
SimpleAccount(const Address& a, const uint256_t& b, const Code& c) :
address(a),
balance(b),
code(c),
nonce(0)
{}
SimpleAccount(
const Address& a, const uint256_t& b, const Code& c, Nonce n) :
address(a),
balance(b),
code(c),
nonce(n)
{}
virtual Address get_address() const override;
void set_address(const Address& a);
virtual uint256_t get_balance() const override;
virtual void set_balance(const uint256_t& b) override;
virtual Nonce get_nonce() const override;
void set_nonce(Nonce n);
virtual void increment_nonce() override;
virtual Code get_code() const override;
virtual void set_code(Code&& c) override;
virtual bool has_code() override;
bool operator==(const Account&) const;
friend void to_json(nlohmann::json&, const SimpleAccount&);
friend void from_json(const nlohmann::json&, SimpleAccount&);
};
void to_json(nlohmann::json&, const SimpleAccount&);
void from_json(const nlohmann::json&, SimpleAccount&);
} // namespace eevm

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

@ -4,7 +4,8 @@
#pragma once
#include "eEVM/globalstate.h"
#include "simplestorage.h"
#include "eEVM/simple/simpleaccount.h"
#include "eEVM/simple/simplestorage.h"
namespace eevm
{
@ -13,24 +14,26 @@ namespace eevm
*/
class SimpleGlobalState : public GlobalState
{
public:
using StateEntry = std::pair<SimpleAccount, SimpleStorage>;
private:
Block currentBlock;
protected:
std::map<uint256_t, std::pair<Account, SimpleStorage>> accounts;
std::map<Address, StateEntry> accounts;
public:
SimpleGlobalState() = default;
explicit SimpleGlobalState(Block b) : currentBlock(std::move(b)) {}
virtual bool exists(const Address& addr) override;
virtual void remove(const Address& addr) override;
AccountState get(const Address& addr) override;
AccountState create(
const Address& addr, const uint256_t& balance, const Code& code) override;
size_t num_accounts() override;
bool exists(const Address& addr);
size_t num_accounts();
virtual const Block& get_current_block() override;
virtual uint256_t get_block_hash(uint8_t offset) override;
@ -39,7 +42,7 @@ namespace eevm
* For tests which require some initial state, allow manual insertion of
* pre-constructed accounts
*/
void insert(std::pair<Account, SimpleStorage> p);
void insert(const StateEntry& e);
friend void to_json(nlohmann::json&, const SimpleGlobalState&);
friend void from_json(const nlohmann::json&, SimpleGlobalState&);

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

@ -22,7 +22,7 @@ namespace eevm
void store(const uint256_t& key, const uint256_t& value) override;
uint256_t load(const uint256_t& key) override;
bool exists(const uint256_t& key) override;
bool exists(const uint256_t& key);
bool remove(const uint256_t& key) override;
bool operator==(const SimpleStorage& that) const;
@ -30,6 +30,7 @@ namespace eevm
friend void to_json(nlohmann::json&, const SimpleStorage&);
friend void from_json(const nlohmann::json&, SimpleStorage&);
};
void to_json(nlohmann::json& j, const SimpleStorage& s);
void from_json(const nlohmann::json& j, SimpleStorage& s);
} // namespace eevm

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

@ -15,7 +15,6 @@ namespace eevm
{
virtual void store(const uint256_t& key, const uint256_t& value) = 0;
virtual uint256_t load(const uint256_t& key) = 0;
virtual bool exists(const uint256_t& key) = 0;
virtual bool remove(const uint256_t& key) = 0;
virtual ~Storage() {}
};

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

@ -120,9 +120,9 @@ eevm::Address deploy_erc20_contract(
// Result of running the compiled constructor is the code that should be the
// contract's body (constructor will also have setup contract's Storage)
contract.acc.code = result;
contract.acc.set_code(std::move(result));
return contract.acc.address;
return contract.acc.get_address();
}
// Get the total token supply by calling totalSupply on the contract_address

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

@ -120,7 +120,7 @@ int main(int argc, char** argv)
std::cout << fmt::format(
"Address {} contains the following bytecode:\n {}",
eevm::to_checksum_address(to),
eevm::to_hex_string(contract.acc.code))
eevm::to_hex_string(contract.acc.get_code()))
<< std::endl;
}

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

@ -1,75 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "eEVM/account.h"
#include "eEVM/exception.h"
#include <ostream>
#include <type_traits>
namespace eevm
{
bool Account::has_code() const
{
return code.empty();
}
void Account::set_code(Code&& code_)
{
// Only set code when it hasn't been set yet
if (has_code())
return;
code = code_;
}
void Account::pay(const uint256_t& amount)
{
if (amount > balance)
throw Exception(
Exception::Type::outOfFunds,
"Insufficient funds to pay (" + to_hex_str(amount) + " > " +
to_hex_str(balance) + ")");
balance -= amount;
}
void Account::pay(Account& r, const uint256_t& amount)
{
pay(amount);
r.balance += amount;
}
bool Account::operator==(const Account& that) const
{
return address == that.address && nonce == that.nonce &&
balance == that.balance && code == that.code;
}
inline std::ostream& operator<<(std::ostream& os, const Account& a)
{
os << nlohmann::json(a).dump(2);
return os;
}
void to_json(nlohmann::json& j, const Account& a)
{
j["address"] = address_to_hex_string(a.address);
j["balance"] = a.balance;
j["nonce"] = to_hex_string(a.nonce);
j["code"] = to_hex_string(a.code);
}
void from_json(const nlohmann::json& j, Account& a)
{
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 eevm

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

@ -36,12 +36,10 @@ namespace eevm
class Program
{
public:
const vector<uint8_t>& code;
const vector<uint8_t> code;
const set<uint64_t> jump_dests;
Program(const vector<uint8_t>& code) :
code(code),
jump_dests(compute_jump_dests(code))
Program(vector<uint8_t>&& c) : code(c), jump_dests(compute_jump_dests(code))
{}
private:
@ -191,7 +189,14 @@ namespace eevm
};
push_context(
caller, callee, move(input), callee.acc.code, call_value, rh, hh, eh);
caller,
callee,
move(input),
callee.acc.get_code(),
call_value,
rh,
hh,
eh);
// run
while (ctxt->get_pc() < ctxt->prog.code.size())
@ -653,7 +658,8 @@ namespace eevm
"Unknown/unsupported Opcode: 0x{:02x}", int{get_op()})
<< endl;
err << fmt::format(
" in contract {}", to_checksum_address(ctxt->as.acc.address))
" in contract {}",
to_checksum_address(ctxt->as.acc.get_address()))
<< endl;
err << fmt::format(" called by {}", to_checksum_address(ctxt->caller))
<< endl;
@ -988,17 +994,18 @@ namespace eevm
void extcodesize()
{
ctxt->s.push(gs.get(pop_addr(ctxt->s)).acc.code.size());
ctxt->s.push(gs.get(pop_addr(ctxt->s)).acc.get_code().size());
}
void extcodecopy()
{
copy_mem(ctxt->mem, gs.get(pop_addr(ctxt->s)).acc.code, Opcode::STOP);
copy_mem(
ctxt->mem, gs.get(pop_addr(ctxt->s)).acc.get_code(), Opcode::STOP);
}
void codesize()
{
ctxt->s.push(ctxt->acc.code.size());
ctxt->s.push(ctxt->acc.get_code().size());
}
void calldataload()
@ -1036,13 +1043,13 @@ namespace eevm
void address()
{
ctxt->s.push(ctxt->acc.address);
ctxt->s.push(ctxt->acc.get_address());
}
void balance()
{
decltype(auto) acc = gs.get(pop_addr(ctxt->s)).acc;
ctxt->s.push(acc.balance);
ctxt->s.push(acc.get_balance());
}
void origin()
@ -1102,7 +1109,7 @@ namespace eevm
topics[i] = ctxt->s.pop();
tx.log_handler.handle(
{ctxt->acc.address, copy_from_mem(offset, size), topics});
{ctxt->acc.get_address(), copy_from_mem(offset, size), topics});
}
void blockhash()
@ -1184,8 +1191,9 @@ namespace eevm
void destroy()
{
gs.get(pop_addr(ctxt->s)).acc.balance += ctxt->acc.balance;
tx.destroy_list.push_back(ctxt->acc.address);
auto recipient = gs.get(pop_addr(ctxt->s));
ctxt->acc.pay_to(recipient.acc, ctxt->acc.get_balance());
tx.destroy_list.push_back(ctxt->acc.get_address());
stop();
}
@ -1194,24 +1202,40 @@ namespace eevm
const auto contractValue = ctxt->s.pop();
const auto offset = ctxt->s.pop64();
const auto size = ctxt->s.pop64();
ctxt->acc.pay(contractValue);
const auto initCode = copy_from_mem(offset, size);
auto initCode = copy_from_mem(offset, size);
const auto newAddress =
generate_address(ctxt->acc.address, ctxt->acc.nonce);
generate_address(ctxt->acc.get_address(), ctxt->acc.get_nonce());
// For contract accounts, the nonce counts the number of
// contract-creations by this account
// TODO: Work out why this fails the test cases
//ctxt->acc.increment_nonce();
decltype(auto) newAcc = gs.create(newAddress, contractValue, {});
// In contract creation, the transaction value is an endowment for the
// newly created account
ctxt->acc.pay_to(newAcc.acc, contractValue);
auto parentContext = ctxt;
auto rh = [&newAcc, parentContext](vector<uint8_t> output) {
newAcc.acc.set_code(move(output));
parentContext->s.push(newAcc.acc.address);
parentContext->s.push(newAcc.acc.get_address());
};
auto hh = [parentContext]() { parentContext->s.push(0); };
auto eh = [parentContext](const Exception&) { parentContext->s.push(0); };
// create new context for init code execution
push_context(ctxt->acc.address, newAcc, {}, initCode, 0, rh, hh, eh);
push_context(
ctxt->acc.get_address(),
newAcc,
{},
std::move(initCode),
0,
rh,
hh,
eh);
}
void call()
@ -1234,7 +1258,7 @@ namespace eevm
}
decltype(auto) callee = gs.get(addr);
ctxt->acc.pay(callee.acc, value);
ctxt->acc.pay_to(callee.acc, value);
if (!callee.acc.has_code())
{
ctxt->s.push(1);
@ -1257,10 +1281,10 @@ namespace eevm
{
case Opcode::CALL:
push_context(
ctxt->acc.address,
ctxt->acc.get_address(),
callee,
move(input),
callee.acc.code,
callee.acc.get_code(),
value,
rh,
hh,
@ -1268,10 +1292,10 @@ namespace eevm
break;
case Opcode::CALLCODE:
push_context(
ctxt->acc.address,
ctxt->acc.get_address(),
ctxt->as,
move(input),
callee.acc.code,
callee.acc.get_code(),
value,
rh,
hh,
@ -1282,7 +1306,7 @@ namespace eevm
ctxt->caller,
ctxt->as,
move(input),
callee.acc.code,
callee.acc.get_code(),
ctxt->call_value,
rh,
hh,

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

@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "eEVM/simple/simpleaccount.h"
#include "eEVM/util.h"
namespace eevm
{
Address SimpleAccount::get_address() const
{
return address;
}
void SimpleAccount::set_address(const Address& a)
{
address = a;
}
uint256_t SimpleAccount::get_balance() const
{
return balance;
}
void SimpleAccount::set_balance(const uint256_t& b)
{
balance = b;
}
Account::Nonce SimpleAccount::get_nonce() const
{
return nonce;
}
void SimpleAccount::set_nonce(Nonce n)
{
nonce = n;
}
void SimpleAccount::increment_nonce()
{
++nonce;
}
Code SimpleAccount::get_code() const
{
return code;
}
void SimpleAccount::set_code(Code&& c)
{
code = c;
}
bool SimpleAccount::has_code()
{
return !get_code().empty();
}
bool SimpleAccount::operator==(const Account& a) const
{
return get_address() == a.get_address() &&
get_balance() == a.get_balance() && get_nonce() == a.get_nonce() &&
get_code() == a.get_code();
}
void to_json(nlohmann::json& j, const SimpleAccount& a)
{
j["address"] = address_to_hex_string(a.address);
j["balance"] = a.balance;
j["nonce"] = to_hex_string(a.nonce);
j["code"] = to_hex_string(a.code);
}
void from_json(const nlohmann::json& j, SimpleAccount& a)
{
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 eevm

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

@ -5,11 +5,6 @@
namespace eevm
{
bool SimpleGlobalState::exists(const Address& addr)
{
return accounts.find(addr) != accounts.end();
}
void SimpleGlobalState::remove(const Address& addr)
{
accounts.erase(addr);
@ -27,11 +22,14 @@ namespace eevm
AccountState SimpleGlobalState::create(
const Address& addr, const uint256_t& balance, const Code& code)
{
const auto acc = accounts.emplace(
addr, std::make_pair(Account(addr, balance, code), SimpleStorage()));
assert(acc.second);
insert({SimpleAccount(addr, balance, code), {}});
return acc.first->second;
return get(addr);
}
bool SimpleGlobalState::exists(const Address& addr)
{
return accounts.find(addr) != accounts.end();
}
size_t SimpleGlobalState::num_accounts()
@ -49,9 +47,11 @@ namespace eevm
return 0u;
}
void SimpleGlobalState::insert(std::pair<Account, SimpleStorage> p)
void SimpleGlobalState::insert(const StateEntry& p)
{
accounts.insert(std::make_pair(p.first.address, p));
const auto ib = accounts.insert(std::make_pair(p.first.get_address(), p));
assert(ib.second);
}
bool operator==(const SimpleGlobalState& l, const SimpleGlobalState& r)
@ -74,12 +74,11 @@ namespace eevm
{
if (j.find("block") != j.end())
assign_j(a.currentBlock, j["block"]);
auto acc = j["accounts"].items();
for (const auto& it : acc)
for (const auto& it : j["accounts"].items())
{
Address addr = it.value()[0];
std::pair<Account, SimpleStorage> p = it.value()[1];
a.accounts.insert(make_pair(addr, p));
const auto& v = it.value();
a.accounts.insert(make_pair(from_hex_str(v[0]), v[1]));
}
}
} // namespace eevm

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

@ -18,12 +18,12 @@ namespace eevm
uint64_t to_uint64(const std::string& s)
{
return strtoull(&s[0], nullptr, 16);
return strtoull(s.c_str(), nullptr, 16);
}
uint64_t to_uint64(const nlohmann::json& j)
{
return to_uint64(string(j));
return to_uint64(j.get<std::string>());
}
vector<uint8_t> to_bytes(const string& _s)

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

@ -18,11 +18,11 @@ using namespace std;
using namespace eevm;
using namespace nlohmann;
pair<Account, SimpleStorage> parseAccount(json::const_iterator& it)
SimpleGlobalState::StateEntry parseAccount(json::const_iterator& it)
{
auto storage = it.value()["storage"];
auto account = it.value().get<Account>();
account.address = from_hex_str(it.key());
auto account = it.value().get<SimpleAccount>();
account.set_address(from_hex_str(it.key()));
return {account, storage};
}
@ -132,8 +132,8 @@ void run_test_case(
for (auto it = post.value().cbegin(); it != post.value().cend(); it++)
{
const auto expected = parseAccount(it);
const auto actual = gs.get(expected.first.address);
CHECK(actual.acc == expected.first);
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);

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

@ -5,6 +5,7 @@
#include "eEVM/disassembler.h"
#include "eEVM/opcode.h"
#include "eEVM/processor.h"
#include "eEVM/simple/simpleaccount.h"
#include "eEVM/simple/simpleglobalstate.h"
#include "eEVM/util.h"
@ -49,46 +50,48 @@ TEST_CASE(
REQUIRE(j == j2);
}
SUBCASE("Using default Account objects")
SUBCASE("Using default SimpleAccount objects")
{
Account a1;
SimpleAccount a1;
nlohmann::json j = a1;
Account a2 = j.get<Account>();
SimpleAccount a2 = j.get<SimpleAccount>();
REQUIRE(a1 == a2);
}
SUBCASE("Using non-default values for Account")
SUBCASE("Using non-default values for SimpleAccount")
{
Account a1;
a1.address = from_hex_str("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6");
a1.nonce = to_uint64(string("0x66"));
SimpleAccount a1(
from_hex_str("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"),
5678,
{0x00, 0x01, 0x10, 0x11},
0x66);
nlohmann::json j = a1;
Account a2 = j;
SimpleAccount a2 = j;
REQUIRE(a1 == a2);
}
SUBCASE("Using partially defined JSON as a source for Account")
SUBCASE("Using partially defined JSON as a source for SimpleAccount")
{
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>();
SimpleAccount a1 = rec.get<SimpleAccount>();
nlohmann::json j2 = a1;
if (rec.find("balance") != rec.end())
CHECK(a1.balance == from_hex_str(j2["balance"]));
CHECK(a1.get_balance() == from_hex_str(j2["balance"]));
if (rec.find("code") != rec.end())
CHECK(a1.code == to_bytes(j2["code"]));
CHECK(a1.get_code() == to_bytes(j2["code"]));
if (rec.find("nonce") != rec.end())
CHECK(a1.nonce == to_uint64(j2["nonce"]));
CHECK(a1.get_nonce() == to_uint64(j2["nonce"]));
if (rec.find("address") != rec.end())
CHECK(a1.address == from_hex_str(j2["address"]));
CHECK(a1.get_address() == from_hex_str(j2["address"]));
}
SUBCASE("Using fully defined JSON as a source for Account")
SUBCASE("Using fully defined JSON as a source for SimpleAccount")
{
auto test_path = string(test_dir) + "/accountFull.json";
const auto j = nlohmann::json::parse(std::ifstream(test_path));
Account a1 = j.get<Account>();
SimpleAccount a1 = j.get<SimpleAccount>();
nlohmann::json j2 = a1;
REQUIRE(j == j2);
}