Light wallet subaddress support

This commit is contained in:
Sidney 2024-04-25 19:58:57 +02:00
parent efb20d1b1e
commit f2a51ebc43
3 changed files with 319 additions and 15 deletions

View File

@ -1597,6 +1597,8 @@ bool wallet2::should_expand(const cryptonote::subaddress_index &index) const
void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
{
hw::device &hwdev = m_account.get_device();
std::vector<cryptonote::subaddress_index> subaddrs;
if (m_subaddress_labels.size() <= index.major)
{
// add new accounts
@ -1614,6 +1616,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
}
m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
m_subaddress_labels[index.major].resize(index.minor + 1);
subaddrs.push_back(index2);
get_account_tags();
}
else if (m_subaddress_labels[index.major].size() <= index.minor)
@ -1629,6 +1632,13 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
m_subaddresses[D] = index2;
}
m_subaddress_labels[index.major].resize(index.minor + 1);
subaddrs.push_back(index2);
}
if (m_light_wallet && !subaddrs.empty()) {
if(light_wallet_upsert_subaddrs(subaddrs)) {
MINFO("Successfully upsert light wallet subaddresses");
} else throw std::runtime_error("Error while upserting light wallet subaddresses");
}
}
//----------------------------------------------------------------------------------------------------
@ -6533,9 +6543,6 @@ boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data()
uint64_t wallet2::balance(uint32_t index_major, bool strict) const
{
uint64_t amount = 0;
if(m_light_wallet)
if(index_major == 0) return m_light_wallet_balance;
else return 0;
for (const auto& i : balance_per_subaddress(index_major, strict))
amount += i.second;
return amount;
@ -10214,7 +10221,9 @@ void wallet2::light_wallet_get_unspent_outs()
string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key);
for(auto &t: m_transfers){
// everoddandeven: m_transfers should e empty, before of previous call of m_transfer.clear()
if(t.get_public_key() == public_key) {
// update spent status
t.m_spent = spent;
add_transfer = false;
break;
@ -10243,6 +10252,8 @@ void wallet2::light_wallet_get_unspent_outs()
td.m_internal_output_index = o.index;
td.m_spent = spent;
td.m_frozen = false;
td.m_subaddr_index.major = o.recipient.maj_i;
td.m_subaddr_index.minor = o.recipient.min_i;
tx_out txout;
txout.target = txout_to_key(public_key);
@ -10388,6 +10399,7 @@ void wallet2::light_wallet_get_address_txs()
address_tx.m_timestamp = t.timestamp;
address_tx.m_coinbase = t.coinbase;
address_tx.m_mempool = t.mempool;
m_light_wallet_address_txs.emplace(tx_hash,address_tx);
// populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs)
@ -10571,9 +10583,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
return key_image == calculated_key_image;
}
std::vector<bool> wallet2::light_wallet_is_key_image_spent(const std::vector<crypto::key_image>& key_images) {
MDEBUG("Getting unspent outs");
std::vector<bool> wallet2::light_wallet_is_key_image_spent(const std::vector<crypto::key_image>& key_images) {
tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
oreq.amount = "0";
@ -10592,7 +10602,7 @@ std::vector<bool> wallet2::light_wallet_is_key_image_spent(const std::vector<cry
THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
m_light_wallet_per_kb_fee = ores.per_kb_fee;
MDEBUG("FOUND " << ores.outputs.size() <<" outputs");
std:vector<bool> spent_list;
for(crypto::key_image key_image : key_images) {
@ -10612,6 +10622,119 @@ std::vector<bool> wallet2::light_wallet_is_key_image_spent(const std::vector<cry
return spent_list;
}
void wallet2::light_wallet_get_subaddrs() {
MINFO("Getting light wallet subaddresses");
tools::COMMAND_RPC_GET_SUBADDRS::request oreq;
tools::COMMAND_RPC_GET_SUBADDRS::response ores;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/get_subaddrs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_subaddrs");
m_light_wallet_subaddrs.clear();
m_light_wallet_accounts.clear();
for(auto subaddr : ores.all_subaddrs) {
for(auto index_range : subaddr.value) {
THROW_WALLET_EXCEPTION_IF(index_range.size() != 2, error::wallet_internal_error, "Invalid index range size");
uint32_t minor = index_range[0];
uint32_t major = index_range[1];
for(;minor <= major; minor++) {
m_light_wallet_subaddrs.push_back({subaddr.key,minor});
}
}
m_light_wallet_accounts.push_back(subaddr.key);
}
MINFO("GOT " << m_light_wallet_accounts.size() << " ACCOUNTS AND " << m_light_wallet_subaddrs.size() << " SUBADDRESSES");
}
bool wallet2::light_wallet_upsert_subaddrs(std::vector<cryptonote::subaddress_index> subaddrs) {
tools::COMMAND_RPC_UPSERT_SUBADDRS::request oreq;
tools::COMMAND_RPC_UPSERT_SUBADDRS::response ores;
std::vector<tools::COMMAND_RPC_UPSERT_SUBADDRS::subaddrs> all_subaddrs;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
oreq.get_all = true;
for (cryptonote::subaddress_index subaddr : subaddrs) {
oreq.subaddrs.key = subaddr.major;
std::vector<uint32_t> index_range;
index_range.push_back(0);
index_range.push_back(subaddr.minor);
oreq.subaddrs.value.push_back(index_range);
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/upsert_subaddrs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "upsert_subaddrs");
all_subaddrs = ores.all_subaddrs;
}
m_light_wallet_subaddrs.clear();
m_light_wallet_accounts.clear();
for(auto subaddr : ores.all_subaddrs) {
for(auto index_range : subaddr.value) {
THROW_WALLET_EXCEPTION_IF(index_range.size() != 2, error::wallet_internal_error, "Invalid index range size");
uint32_t minor = index_range[0];
uint32_t major = index_range[1];
for(;minor <= major; minor++) {
m_light_wallet_subaddrs.push_back({subaddr.key,minor});
}
}
m_light_wallet_accounts.push_back(subaddr.key);
}
return true;
}
bool wallet2::light_wallet_provision_subaddrs(uint32_t maj_i, uint32_t min_i, uint32_t n_maj, uint32_t n_min) {
tools::COMMAND_RPC_PROVISION_SUBADDRS::request oreq;
tools::COMMAND_RPC_PROVISION_SUBADDRS::response ores;
std::vector<tools::COMMAND_RPC_PROVISION_SUBADDRS::subaddrs> all_subaddrs;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
oreq.get_all = true;
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/provision_subaddrs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "provision_subaddrs");
m_light_wallet_subaddrs.clear();
m_light_wallet_accounts.clear();
for(auto subaddr : ores.all_subaddrs) {
for(auto index_range : subaddr.value) {
THROW_WALLET_EXCEPTION_IF(index_range.size() != 2, error::wallet_internal_error, "Invalid index range size");
uint32_t minor = index_range[0];
uint32_t major = index_range[1];
for(;minor <= major; minor++) {
m_light_wallet_subaddrs.push_back({subaddr.key,minor});
}
}
m_light_wallet_accounts.push_back(subaddr.key);
}
return true;
}
// Another implementation of transaction creation that is hopefully better
// While there is anything left to pay, it goes through random outputs and tries
// to fill the next destination/amount. If it fully fills it, it will use the

View File

@ -1634,7 +1634,12 @@ private:
}
std::vector<bool> light_wallet_is_key_image_spent(const std::vector<crypto::key_image>& key_images);
std::vector<cryptonote::subaddress_index> m_light_wallet_subaddrs;
std::vector<uint32_t> m_light_wallet_accounts;
bool light_wallet_supports_subaddrs();
void light_wallet_get_subaddrs();
bool light_wallet_provision_subaddrs(uint32_t maj_i, uint32_t min_i, uint32_t n_maj, uint32_t n_min);
bool light_wallet_upsert_subaddrs(std::vector<cryptonote::subaddress_index> subaddrs);
/*
* "attributes" are a mechanism to store an arbitrary number of string values
* on the level of the wallet as a whole, identified by keys. Their introduction,

View File

@ -49,13 +49,28 @@ namespace tools
};
typedef epee::misc_utils::struct_init<request_t> request;
struct address_meta {
uint32_t maj_i;
uint32_t min_i;
BEGIN_SERIALIZE_OBJECT()
FIELD(maj_i)
FIELD(min_i)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(maj_i)
KV_SERIALIZE(min_i)
END_KV_SERIALIZE_MAP()
};
struct spent_output {
uint64_t amount;
std::string key_image;
std::string tx_pub_key;
uint64_t out_index;
uint32_t mixin;
address_meta sender;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
@ -63,6 +78,7 @@ namespace tools
KV_SERIALIZE(tx_pub_key)
KV_SERIALIZE(out_index)
KV_SERIALIZE(mixin)
KV_SERIALIZE_OPT(sender, (address_meta)({0, 0}))
END_KV_SERIALIZE_MAP()
};
@ -136,6 +152,21 @@ namespace tools
};
typedef epee::misc_utils::struct_init<request_t> request;
struct address_meta {
uint32_t maj_i;
uint32_t min_i;
BEGIN_SERIALIZE_OBJECT()
FIELD(maj_i)
FIELD(min_i)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(maj_i)
KV_SERIALIZE(min_i)
END_KV_SERIALIZE_MAP()
};
struct spent_output
{
uint64_t amount;
@ -143,13 +174,15 @@ namespace tools
std::string tx_pub_key;
uint64_t out_index;
uint32_t mixin;
address_meta sender;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(key_image)
KV_SERIALIZE(tx_pub_key)
KV_SERIALIZE(out_index)
KV_SERIALIZE(mixin)
KV_SERIALIZE_OPT(sender, (address_meta)({0, 0}))
END_KV_SERIALIZE_MAP()
};
@ -178,7 +211,6 @@ namespace tools
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_GET_UNSPENT_OUTS
{
@ -202,7 +234,21 @@ namespace tools
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct address_meta {
uint32_t maj_i;
uint32_t min_i;
BEGIN_SERIALIZE_OBJECT()
FIELD(maj_i)
FIELD(min_i)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(maj_i)
KV_SERIALIZE(min_i)
END_KV_SERIALIZE_MAP()
};
struct output {
uint64_t amount;
@ -215,8 +261,8 @@ namespace tools
std::string tx_prefix_hash;
std::vector<std::string> spend_key_images;
uint64_t timestamp;
uint64_t height;
uint64_t height;
address_meta recipient;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
@ -229,7 +275,8 @@ namespace tools
KV_SERIALIZE(tx_prefix_hash)
KV_SERIALIZE(spend_key_images)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(height)
KV_SERIALIZE(height)
KV_SERIALIZE_OPT(recipient, (address_meta)({0, 0}))
END_KV_SERIALIZE_MAP()
};
@ -364,4 +411,133 @@ namespace tools
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_PROVISION_SUBADDRS
{
typedef std::vector<uint32_t> index_range;
struct subaddrs {
uint32_t key;
std::vector<index_range> value;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(key)
KV_SERIALIZE(value)
END_KV_SERIALIZE_MAP()
};
struct request_t
{
std::string address;
std::string view_key;
uint32_t maj_i;
uint32_t min_i;
uint32_t n_maj;
uint32_t n_min;
bool get_all;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
KV_SERIALIZE(maj_i)
KV_SERIALIZE(min_i)
KV_SERIALIZE(n_maj)
KV_SERIALIZE(n_min)
KV_SERIALIZE(get_all)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::vector<subaddrs> new_subaddrs;
std::vector<subaddrs> all_subaddrs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(new_subaddrs)
KV_SERIALIZE(all_subaddrs)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_UPSERT_SUBADDRS
{
typedef std::vector<uint32_t> index_range;
struct subaddrs {
uint32_t key;
std::vector<index_range> value;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(key)
KV_SERIALIZE(value)
END_KV_SERIALIZE_MAP()
};
struct request_t
{
std::string address;
std::string view_key;
subaddrs subaddrs;
bool get_all;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
KV_SERIALIZE(subaddrs)
KV_SERIALIZE(get_all)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::vector<subaddrs> new_subaddrs;
std::vector<subaddrs> all_subaddrs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(new_subaddrs)
KV_SERIALIZE(all_subaddrs)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_GET_SUBADDRS
{
struct request_t
{
std::string address;
std::string view_key;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
typedef std::vector<uint32_t> index_range;
struct subaddrs {
uint32_t key;
std::vector<index_range> value;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(key)
KV_SERIALIZE(value)
END_KV_SERIALIZE_MAP()
};
struct response_t
{
std::vector<subaddrs> all_subaddrs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(all_subaddrs)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
}