diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 85e2c8cf1..c936f481e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -859,6 +859,8 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("rescan_bc", boost::bind(&simple_wallet::rescan_blockchain, this, _1), tr("Rescan blockchain from scratch")); m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("Set an arbitrary string note for a txid")); m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), tr("Get a string note for a txid")); + m_cmd_binder.set_handler("set_description", boost::bind(&simple_wallet::set_description, this, _1), tr("Set an arbitrary description for the wallet")); + m_cmd_binder.set_handler("get_description", boost::bind(&simple_wallet::get_description, this, _1), tr("Get the description of the wallet ")); m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), tr("Show wallet status information")); m_cmd_binder.set_handler("wallet_info", boost::bind(&simple_wallet::wallet_info, this, _1), tr("Show wallet information")); m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), tr("Sign the contents of a file")); @@ -5026,6 +5028,39 @@ bool simple_wallet::get_tx_note(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::set_description(const std::vector &args) +{ + // 0 arguments allowed, for setting the description to empty string + + std::string description = ""; + for (size_t n = 0; n < args.size(); ++n) + { + if (n > 0) + description += " "; + description += args[n]; + } + m_wallet->set_description(description); + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::get_description(const std::vector &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("usage: get_description"); + return true; + } + + std::string description = m_wallet->get_description(); + if (description.empty()) + success_msg_writer() << tr("no description found"); + else + success_msg_writer() << tr("description found: ") << description; + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::status(const std::vector &args) { uint64_t local_height = m_wallet->get_blockchain_current_height(); @@ -5054,6 +5089,7 @@ bool simple_wallet::status(const std::vector &args) bool simple_wallet::wallet_info(const std::vector &args) { message_writer() << tr("Filename: ") << m_wallet->get_wallet_file(); + message_writer() << tr("Description: ") << m_wallet->get_description(); message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); message_writer() << tr("Watch only: ") << (m_wallet->watch_only() ? tr("Yes") : tr("No")); message_writer() << tr("Testnet: ") << (m_wallet->testnet() ? tr("Yes") : tr("No")); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 6edd41c32..639cee642 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -169,6 +169,8 @@ namespace cryptonote bool refresh_main(uint64_t start_height, bool reset = false, bool is_init = false); bool set_tx_note(const std::vector &args); bool get_tx_note(const std::vector &args); + bool set_description(const std::vector &args); + bool get_description(const std::vector &args); bool status(const std::vector &args); bool wallet_info(const std::vector &args); bool set_default_priority(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index adbe267eb..f0eaf2331 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6257,6 +6257,29 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const return i->second; } +void wallet2::set_attribute(const std::string &key, const std::string &value) +{ + m_attributes[key] = value; +} + +std::string wallet2::get_attribute(const std::string &key) const +{ + std::unordered_map::const_iterator i = m_attributes.find(key); + if (i == m_attributes.end()) + return std::string(); + return i->second; +} + +void wallet2::set_description(const std::string &description) +{ + set_attribute(ATTRIBUTE_DESCRIPTION, description); +} + +std::string wallet2::get_description() const +{ + return get_attribute(ATTRIBUTE_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 9178d18ad..f1e12a700 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -618,6 +618,9 @@ namespace tools a & m_subaddresses_inv; a & m_subaddress_labels; a & m_additional_tx_keys; + if(ver < 21) + return; + a & m_attributes; } /*! @@ -703,6 +706,9 @@ namespace tools void set_tx_note(const crypto::hash &txid, const std::string ¬e); std::string get_tx_note(const crypto::hash &txid) const; + void set_description(const std::string &description); + std::string get_description() const; + 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; @@ -757,6 +763,25 @@ namespace tools // check if key image is ours bool light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index); + /* + * "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, + * technically the unordered map m_attributes stored as part of a wallet file, + * led to a new wallet file version, but now new singular pieces of info may be added + * without the need for a new version. + * + * The first and so far only value stored as such an attribute is the description. + * It's stored under the standard key ATTRIBUTE_DESCRIPTION (see method set_description). + * + * The mechanism is open to all clients and allows them to use it for storing basically any + * single string values in a wallet. To avoid the problem that different clients possibly + * overwrite or misunderstand each other's attributes, a two-part key scheme is + * proposed: . + */ + const char* const ATTRIBUTE_DESCRIPTION = "wallet2.description"; + void set_attribute(const std::string &key, const std::string &value); + std::string get_attribute(const std::string &key) const; + private: /*! * \brief Stores wallet information to wallet file. @@ -835,6 +860,7 @@ namespace tools std::unordered_map m_subaddresses_inv; std::vector> m_subaddress_labels; std::unordered_map m_tx_notes; + std::unordered_map m_attributes; std::vector m_address_book; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value @@ -882,7 +908,7 @@ namespace tools std::unordered_map > m_key_image_cache; }; } -BOOST_CLASS_VERSION(tools::wallet2, 20) +BOOST_CLASS_VERSION(tools::wallet2, 21) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index acd5357ed..9e6a97bdc 100755 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1273,6 +1273,35 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + m_wallet->set_attribute(req.key, req.value); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + res.value = m_wallet->get_attribute(req.key); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::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 f2d98df6f..b38726cb7 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -90,6 +90,8 @@ namespace tools MAP_JON_RPC_WE("rescan_blockchain", on_rescan_blockchain, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN) MAP_JON_RPC_WE("set_tx_notes", on_set_tx_notes, wallet_rpc::COMMAND_RPC_SET_TX_NOTES) MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES) + MAP_JON_RPC_WE("set_attribute", on_set_attribute, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE) + MAP_JON_RPC_WE("get_attribute", on_get_attribute, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE) MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID) MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN) @@ -134,6 +136,8 @@ namespace tools bool on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er); bool on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er); bool on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er); + bool on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er); + bool on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er); bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er); bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er); bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::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 f652fa7ff..ffc2e2d49 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -739,6 +739,48 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_SET_ATTRIBUTE + { + struct request + { + std::string key; + std::string value; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key) + KV_SERIALIZE(value) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_ATTRIBUTE + { + struct request + { + + std::string key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string value; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(value) + END_KV_SERIALIZE_MAP() + }; + }; + struct transfer_entry { std::string txid;