improvements to wallet address formatting (#64)
* bumped version to v1.0.0 * added fast sync points up to 325,000 * added fast sync points up to 325,000 (part 2) * initial import of CLI wallet improvements for address printing * updated wallet address display in various places --------- Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
This commit is contained in:
committed by
GitHub
parent
c03402d525
commit
c07698fcc8
@@ -1,6 +1,6 @@
|
||||
# Salvium One v1.0.0
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
Portions Copyright (c) 2012-2013 The Cryptonote developers.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -6514,7 +6514,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
|
||||
static const char expected_block_hashes_hash[] = "1cf6e8892e0512c246cef62610ccf524f30f484e307ae01959a5a7dd166aa328";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
|
||||
@@ -6472,7 +6472,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
|
||||
if (!detailed || balance_per_subaddress.empty())
|
||||
continue;
|
||||
success_msg_writer() << tr("Balance per address:");
|
||||
success_msg_writer() << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
|
||||
success_msg_writer() << boost::format("%16s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
|
||||
std::vector<tools::wallet2::transfer_details> transfers;
|
||||
m_wallet->get_transfers(transfers);
|
||||
for (const auto& i : balance_per_subaddress)
|
||||
@@ -6480,9 +6480,9 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
|
||||
carrot::subaddress_index_extended subaddr = {{m_current_subaddress_account, i.first},
|
||||
is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
|
||||
cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
|
||||
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 6);
|
||||
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 8);
|
||||
uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; });
|
||||
success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
|
||||
success_msg_writer() << boost::format(tr("%8u %8s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -9902,7 +9902,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
payment_id = payment_id.substr(0,16);
|
||||
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
|
||||
std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
|
||||
std::string destination = m_wallet->get_subaddress_as_str({{m_current_subaddress_account, pd.m_subaddr_index.minor}, pd.m_is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
const std::string type =
|
||||
pd.m_tx_type == cryptonote::transaction_type::STAKE ? "stake" :
|
||||
pd.m_coinbase ? tr("block") : tr("in");
|
||||
@@ -10105,7 +10105,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (!destinations.empty())
|
||||
destinations += ", ";
|
||||
destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_money(output.second);
|
||||
destinations += ((transfer.direction == "in" || transfer.direction == "block") ? output.first.substr(0, 8) : output.first) + ":" + print_money(output.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10631,7 +10631,7 @@ std::string simple_wallet::get_prompt() const
|
||||
{
|
||||
if (m_locked)
|
||||
return std::string("[") + tr("locked due to inactivity") + "]";
|
||||
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
|
||||
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 8);
|
||||
std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
|
||||
if (!m_wallet->check_connection(NULL))
|
||||
prompt += tr(" (no daemon)");
|
||||
@@ -10854,7 +10854,7 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
success_msg_writer() << tr("Accounts with tag: ") << tag;
|
||||
success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
|
||||
}
|
||||
success_msg_writer() << boost::format(" %15s %21s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
|
||||
success_msg_writer() << boost::format(" %18s %21s %21s %6s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
|
||||
std::map<std::string, std::pair<uint64_t, uint64_t>> total_balances;
|
||||
std::vector<std::string> asset_types_in_wallet = m_wallet->list_asset_types();
|
||||
for (const auto& asset: asset_types_in_wallet) {
|
||||
@@ -10868,10 +10868,10 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
auto unlocked_balance = m_wallet->unlocked_balance(account_index, asset, false);
|
||||
if (balance == 0)
|
||||
continue;
|
||||
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s %21s"))
|
||||
success_msg_writer() << boost::format(tr(" %c%8u %8s %21s %21s %6s %21s"))
|
||||
% (m_current_subaddress_account == account_index ? '*' : ' ')
|
||||
% account_index
|
||||
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
|
||||
% m_wallet->get_subaddress_as_str({{account_index, 0}, carrot::AddressDeriveType::Auto}).substr(0, 8)
|
||||
% print_money(balance)
|
||||
% print_money(unlocked_balance)
|
||||
% asset
|
||||
@@ -10882,9 +10882,9 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
if (total_balance > 0)
|
||||
total_balances[asset] = std::pair<uint64_t, uint64_t>(total_balance, total_unlocked_balance);
|
||||
}
|
||||
success_msg_writer() << tr("------------------------------------------------------------------------------------");
|
||||
success_msg_writer() << tr("------------------------------------------------------------------------------------------------------");
|
||||
for (const auto& it: total_balances)
|
||||
success_msg_writer() << boost::format(tr("%15s %21s %21s %15s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
|
||||
success_msg_writer() << boost::format(tr("%18s %21s %21s %6s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||
@@ -10900,16 +10900,35 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
|
||||
std::vector<std::string> local_args = args;
|
||||
tools::wallet2::transfer_container transfers;
|
||||
m_wallet->get_transfers(transfers);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
auto print_address_sub = [this, &transfers, is_carrot](uint32_t index)
|
||||
auto print_address_sub = [this, &transfers](uint32_t index, bool cryptonote = true, bool carrot = true)
|
||||
{
|
||||
bool used = std::find_if(
|
||||
transfers.begin(), transfers.end(),
|
||||
[this, &index](const tools::wallet2::transfer_details& td) {
|
||||
return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
|
||||
}) != transfers.end();
|
||||
success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
|
||||
if (!cryptonote) {
|
||||
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "");
|
||||
} else if (!carrot) {
|
||||
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "");
|
||||
} else {
|
||||
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s\n %96s\n"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "")
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot});
|
||||
}
|
||||
//success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
|
||||
};
|
||||
|
||||
uint32_t index = 0;
|
||||
@@ -10917,11 +10936,18 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
|
||||
{
|
||||
print_address_sub(index);
|
||||
}
|
||||
else if (local_args.size() == 1 && local_args[0] == "all")
|
||||
else if (local_args[0] == "all")
|
||||
{
|
||||
local_args.erase(local_args.begin());
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index);
|
||||
if (local_args.size() == 0)
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index);
|
||||
else if (local_args[0] == "cn")
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index, true, false);
|
||||
else
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index, false, true);
|
||||
}
|
||||
else if (local_args[0] == "new")
|
||||
{
|
||||
@@ -11321,7 +11347,9 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
|
||||
}
|
||||
message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
|
||||
message_writer() << tr("Description: ") << description;
|
||||
message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
message_writer() << tr("CN Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
message_writer() << tr("Carrot Address: ") << m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
|
||||
|
||||
std::string type;
|
||||
if (m_wallet->watch_only())
|
||||
type = tr("Watch only");
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0-rc6"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.4"
|
||||
#define DEF_MONERO_RELEASE_NAME "One"
|
||||
|
||||
+41
-2
@@ -1584,11 +1584,24 @@ crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::su
|
||||
return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr) const
|
||||
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr_const) const
|
||||
{
|
||||
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
|
||||
// Make a non-const copy of subaddr
|
||||
carrot::subaddress_index_extended subaddr(subaddr_const);
|
||||
|
||||
// Handle "auto" - the account class doesn't know the HF version, so this is the cleanest solution
|
||||
if (subaddr.derive_type == carrot::AddressDeriveType::Auto) {
|
||||
|
||||
// Get the HF version
|
||||
uint32_t hf_version = estimate_current_hard_fork();
|
||||
THROW_WALLET_EXCEPTION_IF(hf_version == 0, error::wallet_internal_error, "unable to estimate the current hard fork - cannot generate subaddress as string");
|
||||
|
||||
// Change the non-const derivation type
|
||||
subaddr.derive_type = (hf_version >= HF_VERSION_CARROT) ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot;
|
||||
}
|
||||
|
||||
// Build the cryptonote::account_public_address
|
||||
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
|
||||
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
|
||||
addr.m_is_carrot = subaddr.derive_type == carrot::AddressDeriveType::Carrot;
|
||||
|
||||
@@ -2936,6 +2949,7 @@ void wallet2::process_new_scanned_transaction(
|
||||
payment.m_coinbase = miner_tx;
|
||||
payment.m_subaddr_index = i.first;
|
||||
payment.m_tx_type = tx.type;
|
||||
payment.m_is_carrot = (tx.version >= TRANSACTION_VERSION_CARROT);
|
||||
if (pool) {
|
||||
if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}))
|
||||
all_same = false;
|
||||
@@ -12312,6 +12326,31 @@ void wallet2::device_show_address(uint32_t account_index, uint32_t address_index
|
||||
hwdev.display_address(subaddress_index{account_index, address_index}, payment_id);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint8_t wallet2::estimate_current_hard_fork() const
|
||||
{
|
||||
// Get the last-seen top height by the wallet
|
||||
uint64_t guessed_height = m_blockchain.size();
|
||||
|
||||
// Get the correct hardfork table, based on current net type
|
||||
const hardfork_t *hfs =
|
||||
(m_nettype == cryptonote::MAINNET) ? mainnet_hard_forks :
|
||||
(m_nettype == cryptonote::TESTNET) ? testnet_hard_forks :
|
||||
stagenet_hard_forks;
|
||||
size_t hfs_count =
|
||||
(m_nettype == cryptonote::MAINNET) ? num_mainnet_hard_forks :
|
||||
(m_nettype == cryptonote::TESTNET) ? num_testnet_hard_forks :
|
||||
num_stagenet_hard_forks;
|
||||
|
||||
// Iterate over the hard fork table, to see what the current fork is for the guessed height
|
||||
for (size_t i = hfs_count-1; i>=0; --i) {
|
||||
if (hfs[i].height <= guessed_height)
|
||||
return hfs[i].version;
|
||||
}
|
||||
|
||||
// return "no value found" to the caller
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint8_t wallet2::get_current_hard_fork()
|
||||
{
|
||||
if (m_offline)
|
||||
|
||||
+15
-2
@@ -496,9 +496,10 @@ private:
|
||||
bool m_coinbase;
|
||||
cryptonote::subaddress_index m_subaddr_index;
|
||||
cryptonote::transaction_type m_tx_type;
|
||||
bool m_is_carrot;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VERSION_FIELD(0)
|
||||
VERSION_FIELD(1)
|
||||
FIELD(m_tx_hash)
|
||||
VARINT_FIELD(m_amount)
|
||||
FIELD(m_asset_type)
|
||||
@@ -510,6 +511,11 @@ private:
|
||||
FIELD(m_coinbase)
|
||||
FIELD(m_subaddr_index)
|
||||
VARINT_FIELD(m_tx_type)
|
||||
if (version < 1) {
|
||||
m_is_carrot = false;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_is_carrot)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -1661,6 +1667,7 @@ private:
|
||||
size_t get_num_transfer_details() const { return m_transfers.size(); }
|
||||
const transfer_details &get_transfer_details(size_t idx) const;
|
||||
|
||||
uint8_t estimate_current_hard_fork() const;
|
||||
uint8_t get_current_hard_fork();
|
||||
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
|
||||
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
|
||||
@@ -2293,7 +2300,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
|
||||
@@ -2622,6 +2629,12 @@ namespace boost
|
||||
a & x.m_amounts;
|
||||
a & x.m_asset_type;
|
||||
a & x.m_tx_type;
|
||||
if (ver < 6)
|
||||
{
|
||||
x.m_is_carrot = false;
|
||||
return;
|
||||
}
|
||||
a & x.m_is_carrot;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace wallet_rpc
|
||||
bool all_accounts;
|
||||
bool all_assets;
|
||||
bool strict;
|
||||
bool carrot_first;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(address_indices)
|
||||
@@ -74,6 +75,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE_OPT(all_accounts, false);
|
||||
KV_SERIALIZE_OPT(all_assets, false);
|
||||
KV_SERIALIZE_OPT(strict, false);
|
||||
KV_SERIALIZE_OPT(carrot_first, false);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
@@ -83,6 +85,7 @@ namespace wallet_rpc
|
||||
uint32_t account_index;
|
||||
uint32_t address_index;
|
||||
std::string address;
|
||||
std::string address_alt;
|
||||
uint64_t balance;
|
||||
uint64_t unlocked_balance;
|
||||
std::string label;
|
||||
@@ -94,6 +97,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(address_alt)
|
||||
KV_SERIALIZE(balance)
|
||||
KV_SERIALIZE(unlocked_balance)
|
||||
KV_SERIALIZE(label)
|
||||
@@ -139,6 +143,7 @@ namespace wallet_rpc
|
||||
{
|
||||
uint32_t account_index;
|
||||
std::vector<uint32_t> address_index;
|
||||
bool carrot_first;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(address_index)
|
||||
@@ -149,12 +154,14 @@ namespace wallet_rpc
|
||||
struct address_info
|
||||
{
|
||||
std::string address;
|
||||
std::string address_alt;
|
||||
std::string label;
|
||||
uint32_t address_index;
|
||||
bool used;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(address_alt)
|
||||
KV_SERIALIZE(label)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE(used)
|
||||
@@ -164,10 +171,12 @@ namespace wallet_rpc
|
||||
struct response_t
|
||||
{
|
||||
std::string address; // to remain compatible with older RPC format
|
||||
std::string address_alt;
|
||||
std::vector<address_info> addresses;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(address_alt)
|
||||
KV_SERIALIZE(addresses)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user