aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/wallet2.cpp40
-rw-r--r--src/wallet/wallet2.h24
-rw-r--r--src/wallet/wallet_rpc_server.cpp70
-rw-r--r--src/wallet/wallet_rpc_server.h8
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h94
5 files changed, 235 insertions, 1 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 04c6ee236..436e7f972 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -7818,6 +7818,46 @@ std::string wallet2::get_description() const
return get_attribute(ATTRIBUTE_DESCRIPTION);
}
+const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
+{
+ // ensure consistency
+ if (m_account_tags.second.size() != get_num_subaddress_accounts())
+ m_account_tags.second.resize(get_num_subaddress_accounts(), "");
+ for (const std::string& tag : m_account_tags.second)
+ {
+ if (!tag.empty() && m_account_tags.first.count(tag) == 0)
+ m_account_tags.first.insert({tag, ""});
+ }
+ for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
+ {
+ if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
+ i = m_account_tags.first.erase(i);
+ else
+ ++i;
+ }
+ return m_account_tags;
+}
+
+void wallet2::set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag)
+{
+ for (uint32_t account_index : account_indices)
+ {
+ THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
+ if (m_account_tags.second[account_index] == tag)
+ MDEBUG("This tag is already assigned to this account");
+ else
+ m_account_tags.second[account_index] = tag;
+ }
+ get_account_tags();
+}
+
+void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
+{
+ THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
+ THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
+ m_account_tags.first[tag] = description;
+}
+
std::string wallet2::sign(const std::string &data) const
{
crypto::hash hash;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 399287c3e..2b46359bf 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -767,6 +767,9 @@ namespace tools
if(ver < 22)
return;
a & m_unconfirmed_payments;
+ if(ver < 23)
+ return;
+ a & m_account_tags;
}
/*!
@@ -863,6 +866,24 @@ namespace tools
void set_description(const std::string &description);
std::string get_description() const;
+ /*!
+ * \brief Get the list of registered account tags.
+ * \return first.Key=(tag's name), first.Value=(tag's label), second[i]=(i-th account's tag)
+ */
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& get_account_tags();
+ /*!
+ * \brief Set a tag to the given accounts.
+ * \param account_indices Indices of accounts.
+ * \param tag Tag's name. If empty, the accounts become untagged.
+ */
+ void set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag);
+ /*!
+ * \brief Set the label of the given tag.
+ * \param tag Tag's name (which must be non-empty).
+ * \param label Tag's description.
+ */
+ void set_account_tag_description(const std::string& tag, const std::string& description);
+
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
@@ -1025,6 +1046,7 @@ namespace tools
std::unordered_map<crypto::hash, std::string> m_tx_notes;
std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book;
+ std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@@ -1077,7 +1099,7 @@ namespace tools
std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 22)
+BOOST_CLASS_VERSION(tools::wallet2, 23)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0482b9dd6..27e77710d 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -418,14 +418,24 @@ namespace tools
res.total_balance = 0;
res.total_unlocked_balance = 0;
cryptonote::subaddress_index subaddr_index = {0,0};
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
+ if (!req.tag.empty() && account_tags.first.count(req.tag) == 0)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = (boost::format(tr("Tag %s is unregistered.")) % req.tag).str();
+ return false;
+ }
for (; subaddr_index.major < m_wallet->get_num_subaddress_accounts(); ++subaddr_index.major)
{
+ if (!req.tag.empty() && req.tag != account_tags.second[subaddr_index.major])
+ continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.balance = m_wallet->balance(subaddr_index.major);
info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major);
info.label = m_wallet->get_subaddress_label(subaddr_index);
+ info.tag = account_tags.second[subaddr_index.major];
res.subaddress_accounts.push_back(info);
res.total_balance += info.balance;
res.total_unlocked_balance += info.unlocked_balance;
@@ -471,6 +481,66 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er)
+ {
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
+ for (const std::pair<std::string, std::string>& p : account_tags.first)
+ {
+ res.account_tags.resize(res.account_tags.size() + 1);
+ auto& info = res.account_tags.back();
+ info.tag = p.first;
+ info.label = p.second;
+ for (size_t i = 0; i < account_tags.second.size(); ++i)
+ {
+ if (account_tags.second[i] == info.tag)
+ info.accounts.push_back(i);
+ }
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, req.tag);
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, "");
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag_description(req.tag, req.description);
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 79f589623..00b1e9644 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -74,6 +74,10 @@ namespace tools
MAP_JON_RPC_WE("get_accounts", on_get_accounts, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS)
MAP_JON_RPC_WE("create_account", on_create_account, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT)
MAP_JON_RPC_WE("label_account", on_label_account, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT)
+ MAP_JON_RPC_WE("get_account_tags", on_get_account_tags, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS)
+ MAP_JON_RPC_WE("tag_accounts", on_tag_accounts, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS)
+ MAP_JON_RPC_WE("untag_accounts", on_untag_accounts, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS)
+ MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION)
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
@@ -136,6 +140,10 @@ namespace tools
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);
bool on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er);
bool on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er);
+ bool on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er);
+ bool on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er);
+ bool on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er);
+ bool on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er);
bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er);
bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 57cc01e27..8d0b4c8db 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -176,7 +176,10 @@ namespace wallet_rpc
{
struct request
{
+ std::string tag; // all accounts if empty, otherwise those accounts with this tag
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -187,6 +190,7 @@ namespace wallet_rpc
uint64_t balance;
uint64_t unlocked_balance;
std::string label;
+ std::string tag;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
@@ -194,6 +198,7 @@ namespace wallet_rpc
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -252,6 +257,95 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_ACCOUNT_TAGS
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct account_tag_info
+ {
+ std::string tag;
+ std::string label;
+ std::vector<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag);
+ KV_SERIALIZE(label);
+ KV_SERIALIZE(accounts);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::vector<account_tag_info> account_tags;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(account_tags)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_TAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::string tag;
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_UNTAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION
+ {
+ struct request
+ {
+ std::string tag;
+ std::string description;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(description)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_HEIGHT
{
struct request