What changed, and why it matters
This commit adds a new read-only wallet command called get_wallet_info that lets an authenticated user ask the wallet for basic metadata such as the wallet file name, address, wallet type, which network it is on, the daemon address, and any configured proxy. It also fixes a small consistency bug where the stored proxy setting was not updated when set_proxy was called. The change is informational only: it does not move, spend, or expose private keys, and it is blocked in restricted RPC mode. The main security consideration is that it makes it slightly easier for a logged-in RPC user to learn the wallet's network setup, which could help target further attacks if the RPC endpoint is already compromised.
No urgent action is required. Operators already running wallet RPC with untrusted users should ensure restricted mode is enabled and RPC access is authenticated and firewalled. Reviewers may want to confirm that get_wallet_info does not inadvertently return sensitive fields such as the full wallet path on shared systems, and that the proxy fix covers all callers of set_proxy.
Security signals we found
New RPC endpoint exposes wallet metadata including daemon address and proxy configuration
Endpoint is denied in restricted RPC mode, reducing exposure for shared/public RPC setups
Proxy state consistency fix removes stale m_proxy value after set_daemon/set_proxy calls
No private key, seed, or spend-authorization logic is touched
Evidence from the diff
The merge adds a new wallet RPC method get_wallet_info and a matching simplewallet display command. The RPC handler returns filename, description, primary address, wallet/seed/network type, daemon_address, daemon_proxy, wallet height, and daemon connection metadata. It is gated by m_restricted and requires an open wallet. A secondary fix moves m_proxy assignment into set_proxy so the stored proxy value stays in sync with the HTTP client’s proxy. No authentication bypass, cryptographic weakness, or transaction logic change is present in the diff.
Changed components
src/wallet/wallet_rpc_server.cppsrc/wallet/wallet_rpc_server.hsrc/wallet/wallet_rpc_server_commands_defs.hsrc/wallet/wallet2.cppsrc/wallet/wallet2.hsrc/simplewallet/simplewallet.cpputils/python-rpc/framework/wallet.pyInspect captured patch +111 / −2
### src/simplewallet/simplewallet.cpp
@@ -9896,6 +9896,8 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
message_writer() << tr("Network type: ") << (
m_wallet->nettype() == cryptonote::TESTNET ? tr("Testnet") :
m_wallet->nettype() == cryptonote::STAGENET ? tr("Stagenet") : tr("Mainnet"));
+ message_writer() << tr("Daemon-Address: ") << m_wallet->get_daemon_address();
+ message_writer() << tr("Daemon-Proxy: ") << m_wallet->get_proxy();
if (ms_status.multisig_is_active)
{
type = tr("Multisig");
### src/wallet/wallet2.cpp
@@ -1425,7 +1425,6 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
}
CHECK_AND_ASSERT_MES(set_proxy(proxy), false, "failed to set proxy address");
- m_proxy = proxy;
const bool changed = m_daemon_address != daemon_address;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
@@ -1446,9 +1445,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
//----------------------------------------------------------------------------------------------------
bool wallet2::set_proxy(const std::string &address)
{
+ m_proxy = address;
return m_http_client->set_proxy(address);
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::get_proxy() const
+{
+ return m_proxy;
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, const std::string &proxy_address, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
{
m_checkpoints.init_default_checkpoints(m_nettype);
### src/wallet/wallet2.h
@@ -825,6 +825,7 @@ namespace tools
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
const std::string &proxy = "");
bool set_proxy(const std::string &address);
+ std::string get_proxy() const;
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
// teardown-only: a permanent stop that also aborts an in-flight daemon request
### src/wallet/wallet_rpc_server.cpp
@@ -629,6 +629,55 @@ namespace tools
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_wallet_info(const wallet_rpc::COMMAND_RPC_GET_WALLET_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_WALLET_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (!m_wallet) return not_open(er);
+ res.filename = m_wallet->get_wallet_file();
+ res.description = m_wallet->get_description();
+ res.address = m_wallet->get_subaddress_as_str({0,0});
+ const auto ms_status{m_wallet->get_multisig_status()};
+ if (m_wallet->watch_only())
+ res.wallet_type = "Watch only";
+ else if (ms_status.multisig_is_active)
+ res.wallet_type = (boost::format("%u/%u multisig%s") % ms_status.threshold % ms_status.total % (ms_status.is_ready ? "" : " (not yet finalized)")).str();
+ else if (m_wallet->is_background_wallet())
+ res.wallet_type = "Background wallet";
+ else
+ res.wallet_type = "Normal";
+ res.network_type = m_wallet->nettype() == cryptonote::TESTNET ? "Testnet"
+ : m_wallet->nettype() == cryptonote::STAGENET ? "Stagenet"
+ : "Mainnet";
+ res.daemon_address = m_wallet->get_daemon_address();
+ res.daemon_proxy = m_wallet->get_proxy();
+ res.wallet_block_height = m_wallet->get_blockchain_current_height();
+
+ res.daemon_block_height = 0;
+ res.daemon_rpc_version = 0;
+ res.daemon_ssl = false;
+ if (m_wallet->check_connection(&res.daemon_rpc_version, &res.daemon_ssl))
+ {
+ std::string err;
+ res.daemon_block_height = m_wallet->get_daemon_blockchain_height(err);
+ if (!err.empty())
+ res.daemon_block_height = 0;
+ }
+
+ if (ms_status.multisig_is_active)
+ res.seed_type = tr("Multisig");
+ else if (m_wallet->is_polyseed())
+ res.seed_type = tr("Polyseed");
+ else
+ res.seed_type = tr("Legacy");
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
### src/wallet/wallet_rpc_server.h
@@ -69,6 +69,7 @@ namespace tools
BEGIN_URI_MAP2()
BEGIN_JSON_RPC_MAP("/json_rpc")
+ MAP_JON_RPC_WE("get_wallet_info", on_get_wallet_info, wallet_rpc::COMMAND_RPC_GET_WALLET_INFO)
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
@@ -171,6 +172,7 @@ namespace tools
END_URI_MAP2()
//json_rpc
+ bool on_get_wallet_info(const wallet_rpc::COMMAND_RPC_GET_WALLET_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_WALLET_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
### src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 34
+#define WALLET_RPC_VERSION_MINOR 35
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -57,6 +57,48 @@ namespace wallet_rpc
#define WALLET_RPC_STATUS_OK "OK"
#define WALLET_RPC_STATUS_BUSY "BUSY"
+ struct COMMAND_RPC_GET_WALLET_INFO
+ {
+ struct request_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string filename;
+ std::string description;
+ std::string address;
+ std::string wallet_type; // Normal | Multisig | Watch-Only | Background
+ std::string seed_type; // Legacy | Multisig | Polyseed
+ std::string network_type;
+ std::string daemon_address;
+ std::string daemon_proxy;
+ std::uint64_t wallet_block_height;
+ std::uint64_t daemon_block_height;
+ std::uint32_t daemon_rpc_version;
+ bool daemon_ssl;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(filename)
+ KV_SERIALIZE(description)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(wallet_type)
+ KV_SERIALIZE(seed_type)
+ KV_SERIALIZE(network_type)
+ KV_SERIALIZE(daemon_address)
+ KV_SERIALIZE(daemon_proxy)
+ KV_SERIALIZE(wallet_block_height)
+ KV_SERIALIZE(daemon_block_height)
+ KV_SERIALIZE(daemon_rpc_version)
+ KV_SERIALIZE(daemon_ssl)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_GET_BALANCE
{
struct request_t
### utils/python-rpc/framework/wallet.py
@@ -39,6 +39,14 @@ def __init__(self, protocol='http', host='127.0.0.1', port=0, idx=0, username=No
self.rpc = JSONRPC('{protocol}://{host}:{port}'.format(protocol=protocol, host=host,
port=port if port else 18090+idx), username, password)
+ def get_wallet_info(self):
+ get_wallet_info = {
+ 'method': 'get_wallet_info',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(get_wallet_info)
+
def transfer(self, destinations, account_index = 0, subaddr_indices = [], priority = 0, ring_size = 0, unlock_time = 0, payment_id = '', get_tx_key = True, do_not_relay = False, get_tx_hex = False, get_tx_metadata = False, subtract_fee_from_outputs = []):
transfer = {
'method': 'transfer',Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.