diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e1b69886e..71b83a7cd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -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 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::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 wallet2::light_wallet_is_key_image_spent(const std::vector& key_images) { - MDEBUG("Getting unspent outs"); - +std::vector wallet2::light_wallet_is_key_image_spent(const std::vector& 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 wallet2::light_wallet_is_key_image_spent(const std::vector spent_list; for(crypto::key_image key_image : key_images) { @@ -10612,6 +10622,119 @@ std::vector wallet2::light_wallet_is_key_image_spent(const std::vector subaddrs) { + tools::COMMAND_RPC_UPSERT_SUBADDRS::request oreq; + tools::COMMAND_RPC_UPSERT_SUBADDRS::response ores; + std::vector 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 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 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 diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 55556f75f..625203020 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1634,7 +1634,12 @@ private: } std::vector light_wallet_is_key_image_spent(const std::vector& key_images); - + std::vector m_light_wallet_subaddrs; + std::vector 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 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, diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h index 743a147f6..d4bcead98 100644 --- a/src/wallet/wallet_light_rpc.h +++ b/src/wallet/wallet_light_rpc.h @@ -49,13 +49,28 @@ namespace tools }; typedef epee::misc_utils::struct_init 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; + 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; }; - //----------------------------------------------- struct COMMAND_RPC_GET_UNSPENT_OUTS { @@ -202,7 +234,21 @@ namespace tools END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init 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 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; }; //----------------------------------------------- + struct COMMAND_RPC_PROVISION_SUBADDRS + { + typedef std::vector index_range; + + struct subaddrs { + uint32_t key; + std::vector 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; + + struct response_t + { + std::vector new_subaddrs; + std::vector 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; + }; + //----------------------------------------------- + struct COMMAND_RPC_UPSERT_SUBADDRS + { + typedef std::vector index_range; + + struct subaddrs { + uint32_t key; + std::vector 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; + + struct response_t + { + std::vector new_subaddrs; + std::vector 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; + }; + //----------------------------------------------- + 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; + + typedef std::vector index_range; + + struct subaddrs { + uint32_t key; + std::vector value; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key) + KV_SERIALIZE(value) + END_KV_SERIALIZE_MAP() + }; + + struct response_t + { + std::vector all_subaddrs; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(all_subaddrs) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + //----------------------------------------------- }