You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
9.7 KiB

#pragma once
#include "monero_headers.h"
#include "tools.h"
#include <boost/optional.hpp>
namespace xmreg
{
using epee::string_tools::pod_to_hex;
using epee::string_tools::hex_to_pod;
using namespace cryptonote;
using namespace crypto;
using namespace std;
class Account
{
public:
enum ADDRESS_TYPE {NONE, PRIMARY, SUBADDRESS};
Account() = default;
Account(network_type _nettype,
address_parse_info const& _addr_info,
secret_key const& _viewkey,
secret_key const& _spendkey);
Account(network_type _nettype,
address_parse_info const& _addr_info,
secret_key const& _viewkey);
Account(network_type _nettype,
address_parse_info const& _addr_info);
Account(network_type _nettype,
string const& _addr_info,
string const& _viewkey,
string const& _spendkey);
Account(network_type _nettype,
string const& _addr_info,
string const& _viewkey)
: Account(_nettype, _addr_info, _viewkey, ""s)
{}
Account(network_type _nettype,
string const& _addr_info)
: Account(_nettype, _addr_info, ""s, ""s)
{}
virtual ADDRESS_TYPE type() const = 0;
inline bool is_subaddress() const
{return type() == SUBADDRESS;}
inline auto const& ai() const
{return addr_info;}
inline auto ai2str() const
{return ai_to_str(addr_info, nettype);}
inline auto const& vk() const
{return viewkey;}
inline auto vk2str() const
{return viewkey ? pod_to_hex(*viewkey) : ""s;}
inline auto const& pvk() const
{return addr_info.address.m_view_public_key;}
inline auto pvk2str() const
{return pod_to_hex(pvk());};
inline auto const& psk() const
{return addr_info.address.m_spend_public_key;}
inline auto psk2str() const
{return pod_to_hex(psk());};
inline auto const& sk() const
{return spendkey;}
inline auto index() const
{return subaddr_idx;}
inline auto const& keys()
{
if (!acc_keys)
{
assert(bool{viewkey});
account_keys akeys;
akeys.m_account_address = ai().address;
akeys.m_view_secret_key = *viewkey;
if (spendkey)
akeys.m_spend_secret_key = *spendkey;
acc_keys = std::move(akeys);
}
return acc_keys;
}
inline void set_index(subaddress_index idx)
{subaddr_idx = std::move(idx);}
inline auto sk2str() const
{return spendkey ? pod_to_hex(*spendkey) : ""s;}
inline auto nt() const
{return nettype;}
explicit operator bool() const
{return type() != NONE;}
static inline string
ai_to_str(address_parse_info const& addr_info,
network_type net_type);
static inline secret_key
parse_secret_key(string const& sk);
friend std::ostream&
operator<<(std::ostream& os, Account const& _acc);
virtual ~Account() = default;
protected:
network_type nettype {network_type::STAGENET};
address_parse_info addr_info {};
boost::optional<secret_key> viewkey;
boost::optional<secret_key> spendkey;
boost::optional<subaddress_index> subaddr_idx;
boost::optional<account_keys> acc_keys;
};
// for now subclassess of the Account don't do much.
// but with time it is expected to change and
// and new functionality dependent on whether
// we have primary or subaddress to be added
// to the respective subclasses.
class EmptyAccount : public Account
{
public:
using Account::Account;
virtual ADDRESS_TYPE type() const override
{return NONE;}
};
class SubaddressAccount : public Account
{
public:
using Account::Account;
virtual inline ADDRESS_TYPE type() const override
{return SUBADDRESS;}
};
class PrimaryAccount : public Account
{
public:
using subaddr_map_t = std::unordered_map<
public_key,
subaddress_index>;
template <typename... T>
PrimaryAccount(T&&... args)
: Account(std::forward<T>(args)...)
{
subaddr_idx = subaddress_index {0, 0};
// register the PrimaryAccount into
// subaddresses map as special case
// for uniform handling of all addresses
subaddresses[psk()] = *subaddr_idx;
}
virtual ADDRESS_TYPE type() const override
{return PRIMARY;}
std::unique_ptr<SubaddressAccount>
gen_subaddress(subaddress_index idx);
std::unique_ptr<SubaddressAccount>
gen_subaddress(uint32_t acc_id, uint32_t addr_id)
{
return gen_subaddress({acc_id, addr_id});
}
unique_ptr<subaddress_index>
has_subaddress(public_key const& pub_spend_key)
{
auto it = subaddresses.find(pub_spend_key);
if (it == subaddresses.end())
return nullptr;
return make_unique<subaddress_index>(it->second);
}
/**
* Unlike above, it does not produce SubaddressAcount
* It just calcualtes public spend key for a subaddress
* with given index and saves it in subaddresses map
*/
subaddr_map_t::const_iterator
add_subaddress_index(uint32_t acc_id, uint32_t addr_id);
/**
* Generates all set public spend keys for
* 50 accounts x 200 subaddresess into
* subaddresses map
*/
void
populate_subaddress_indices(uint32_t last_acc_id = 50);
auto begin() { return subaddresses.begin(); }
auto begin() const { return subaddresses.cbegin(); }
auto end() { return subaddresses.end(); }
auto end() const { return subaddresses.cend(); }
private:
subaddr_map_t subaddresses;
};
// account_factory functions are helper functions
// to easly create Account objects through uniqute_ptr
static unique_ptr<Account>
make_account()
{
return make_unique<EmptyAccount>();
}
template <typename... T>
static unique_ptr<Account>
make_account(string const& addr_str,
T&&... args)
{
auto&& net_and_addr_type
= nettype_based_on_address(addr_str);
if (net_and_addr_type.first == network_type::UNDEFINED)
{
return nullptr;
}
if (net_and_addr_type.second == address_type::SUBADDRESS)
return make_unique<SubaddressAccount>(
net_and_addr_type.first,
addr_str,
std::forward<T>(args)...);
else if (net_and_addr_type.second == address_type::REGULAR
|| net_and_addr_type.second == address_type::INTEGRATED)
return make_unique<PrimaryAccount>(
net_and_addr_type.first,
addr_str,
std::forward<T>(args)...);
return nullptr;
}
template <typename... T>
static unique_ptr<Account>
make_account(network_type net_type,
address_parse_info const& addr_info,
T&&... args)
{
if (!crypto::check_key(addr_info.address.m_view_public_key)
|| !crypto::check_key(addr_info.address.m_spend_public_key))
return nullptr;
if (addr_info.is_subaddress)
return make_unique<SubaddressAccount>(
net_type, addr_info,
std::forward<T>(args)...);
else
return make_unique<PrimaryAccount>(
net_type, addr_info,
std::forward<T>(args)...);
}
template <typename... T>
static unique_ptr<Account>
make_account(subaddress_index idx, T&&... args)
{
auto acc = make_account(std::forward<T>(args)...);
if (!acc)
return nullptr;
if (acc->is_subaddress())
acc->set_index(std::move(idx));
return acc;
}
template <typename... T>
static unique_ptr<PrimaryAccount>
make_primaryaccount(string const& addr_str,
T&&... args)
{
auto&& net_and_addr_type
= nettype_based_on_address(addr_str);
if (net_and_addr_type.first == network_type::UNDEFINED)
{
return nullptr;
}
if (net_and_addr_type.second == address_type::REGULAR
|| net_and_addr_type.second == address_type::INTEGRATED)
{
auto pacc = make_unique<PrimaryAccount>(
net_and_addr_type.first,
addr_str,
std::forward<T>(args)...);
pacc->populate_subaddress_indices();
return pacc;
}
return nullptr;
}
template <typename... T>
static unique_ptr<Account>
make_primaryaccount(network_type net_type,
address_parse_info const& addr_info,
T&&... args)
{
if (!crypto::check_key(addr_info.address.m_view_public_key)
|| !crypto::check_key(addr_info.address.m_spend_public_key))
return nullptr;
return make_unique<PrimaryAccount>(
net_type, addr_info,
std::forward<T>(args)...);
}
unique_ptr<SubaddressAccount>
create(PrimaryAccount const& acc, subaddress_index idx);
inline secret_key
Account::parse_secret_key(string const& sk)
{
secret_key k;
if (!hex_to_pod(sk, k))
throw std::runtime_error("Cant parse secret key: " + sk);
return k;
}
inline string
Account::ai_to_str(address_parse_info const& addr_info,
network_type net_type)
{
return get_account_address_as_str(
net_type,
addr_info.is_subaddress,
addr_info.address);
}
inline std::ostream&
operator<<(std::ostream& os, Account const& _acc)
{
string subaddr_str {"n/a"};
if (_acc.subaddr_idx)
{
stringstream ss;
ss << *_acc.subaddr_idx;
subaddr_str = ss.str();
}
string spendkey_str {"N/A"};
if (_acc.sk())
{
spendkey_str = _acc.sk2str();
}
return os << "nt:" << static_cast<size_t>(_acc.nettype)
<< "," << subaddr_str
<< ",a:" << _acc.ai2str()
<< ",v:" << _acc.vk2str()
<< ",s:" << spendkey_str;
}
}