wallet: new rescan_spent command to update outputs' spent status
This obsoletes the need for a lengthy blockchain rescan when a transaction doesn't end up in the chain after being accepted by the daemon, or any other reason why the wallet's idea of spent and unspent outputs gets out of sync from the blockchain's.
This commit is contained in:
parent
01e81205e0
commit
aa5bc351d4
5 changed files with 92 additions and 0 deletions
|
@ -364,6 +364,7 @@ simple_wallet::simple_wallet()
|
||||||
m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Get spendkey"));
|
m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Get spendkey"));
|
||||||
m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Get deterministic seed"));
|
m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Get deterministic seed"));
|
||||||
m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("available options: seed language - Set wallet seed langage; always-confirm-transfers <1|0> - whether to confirm unsplit txes"));
|
m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("available options: seed language - Set wallet seed langage; always-confirm-transfers <1|0> - whether to confirm unsplit txes"));
|
||||||
|
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
|
||||||
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
|
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
@ -1253,7 +1254,46 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
|
||||||
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (!try_connect_to_daemon())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_wallet->rescan_spent();
|
||||||
|
}
|
||||||
|
catch (const tools::error::daemon_busy&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("daemon is busy. Please try later");
|
||||||
|
}
|
||||||
|
catch (const tools::error::no_connection_to_daemon&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("no connection to daemon. Please, make sure daemon is running.");
|
||||||
|
}
|
||||||
|
catch (const tools::error::is_key_image_spent_error&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("failed to get spent status");
|
||||||
|
}
|
||||||
|
catch (const tools::error::wallet_rpc_error& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unknown RPC error: " << e.to_string());
|
||||||
|
fail_msg_writer() << tr("RPC error: ") << e.what();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("unexpected error: " << e.what());
|
||||||
|
fail_msg_writer() << tr("unexpected error: ") << e.what();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unknown error");
|
||||||
|
fail_msg_writer() << tr("unknown error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::string> &args_)
|
bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::string> &args_)
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,6 +120,7 @@ namespace cryptonote
|
||||||
bool save(const std::vector<std::string> &args);
|
bool save(const std::vector<std::string> &args);
|
||||||
bool save_watch_only(const std::vector<std::string> &args);
|
bool save_watch_only(const std::vector<std::string> &args);
|
||||||
bool set_variable(const std::vector<std::string> &args);
|
bool set_variable(const std::vector<std::string> &args);
|
||||||
|
bool rescan_spent(const std::vector<std::string> &args);
|
||||||
bool set_log(const std::vector<std::string> &args);
|
bool set_log(const std::vector<std::string> &args);
|
||||||
|
|
||||||
uint64_t get_daemon_blockchain_height(std::string& err);
|
uint64_t get_daemon_blockchain_height(std::string& err);
|
||||||
|
|
|
@ -961,6 +961,47 @@ void wallet2::get_payments(std::list<std::pair<crypto::hash,wallet2::payment_det
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::rescan_spent()
|
||||||
|
{
|
||||||
|
std::vector<std::string> key_images;
|
||||||
|
|
||||||
|
// make a list of key images for all our outputs
|
||||||
|
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||||
|
{
|
||||||
|
const transfer_details& td = m_transfers[i];
|
||||||
|
key_images.push_back(string_tools::pod_to_hex(td.m_key_image));
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
|
||||||
|
COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||||
|
req.key_images = key_images;
|
||||||
|
bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/is_key_image_spent", req, daemon_resp, m_http_client, 200000);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != key_images.size(), error::wallet_internal_error,
|
||||||
|
"daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
|
||||||
|
std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size()));
|
||||||
|
|
||||||
|
// update spent status
|
||||||
|
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||||
|
{
|
||||||
|
transfer_details& td = m_transfers[i];
|
||||||
|
if (td.m_spent != daemon_resp.spent_status[i])
|
||||||
|
{
|
||||||
|
if (td.m_spent)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Marking output " << i << " as unspent, it was marked as spent");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Marking output " << i << " as spent, it was marked as unspent");
|
||||||
|
}
|
||||||
|
td.m_spent = daemon_resp.spent_status[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
|
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
|
||||||
{
|
{
|
||||||
if(!is_tx_spendtime_unlocked(td.m_tx.unlock_time))
|
if(!is_tx_spendtime_unlocked(td.m_tx.unlock_time))
|
||||||
|
|
|
@ -235,6 +235,7 @@ namespace tools
|
||||||
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
||||||
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height) const;
|
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height) const;
|
||||||
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
||||||
|
void rescan_spent();
|
||||||
template <class t_archive>
|
template <class t_archive>
|
||||||
inline void serialize(t_archive &a, const unsigned int ver)
|
inline void serialize(t_archive &a, const unsigned int ver)
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,6 +73,7 @@ namespace tools
|
||||||
// wallet_rpc_error *
|
// wallet_rpc_error *
|
||||||
// daemon_busy
|
// daemon_busy
|
||||||
// no_connection_to_daemon
|
// no_connection_to_daemon
|
||||||
|
// is_key_image_spent_error
|
||||||
// wallet_files_doesnt_correspond
|
// wallet_files_doesnt_correspond
|
||||||
//
|
//
|
||||||
// * - class with protected ctor
|
// * - class with protected ctor
|
||||||
|
@ -574,6 +575,14 @@ namespace tools
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
struct is_key_image_spent_error : public wallet_rpc_error
|
||||||
|
{
|
||||||
|
explicit is_key_image_spent_error(std::string&& loc, const std::string& request)
|
||||||
|
: wallet_rpc_error(std::move(loc), "error from is_key_image_spent call", request)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
struct wallet_files_doesnt_correspond : public wallet_logic_error
|
struct wallet_files_doesnt_correspond : public wallet_logic_error
|
||||||
{
|
{
|
||||||
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
|
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
|
||||||
|
|
Loading…
Reference in a new issue