device/trezor: ask for KI sync on first refresh
When doing a first refresh on HW-token based wallet KI sync is required if money were received. Received money may indicate wallet was already used before the restore I.e., some transaction could have been already sent from the wallet. The spent UTXO would not be detected as spent which could lead to double spending errors on submitting a new transaction. Thus if the wallet is HW-token based with the cold signing protocol and the first refresh detected received money the user is asked to perform the key image sync.
This commit is contained in:
parent
d21dad70dd
commit
9cf636af69
|
@ -4341,6 +4341,29 @@ void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string
|
||||||
passphrase = pwd_container->password();
|
passphrase = pwd_container->password();
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money)
|
||||||
|
{
|
||||||
|
// Key image sync after the first refresh
|
||||||
|
if (!m_wallet->get_account().get_device().has_tx_cold_sign()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!received_money || m_wallet->get_device_last_key_image_sync() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished first refresh for HW device and money received -> KI sync
|
||||||
|
message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received money. hw_key_images_sync is needed. ");
|
||||||
|
|
||||||
|
std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
|
||||||
|
if (std::cin.eof() || !command_line::is_yes(accepted)) {
|
||||||
|
message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_images_sync_intern();
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
|
bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
|
||||||
{
|
{
|
||||||
if (!try_connect_to_daemon(is_init))
|
if (!try_connect_to_daemon(is_init))
|
||||||
|
@ -4358,13 +4381,14 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||||
message_writer() << tr("Starting refresh...");
|
message_writer() << tr("Starting refresh...");
|
||||||
|
|
||||||
uint64_t fetched_blocks = 0;
|
uint64_t fetched_blocks = 0;
|
||||||
|
bool received_money = false;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||||
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks);
|
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money);
|
||||||
ok = true;
|
ok = true;
|
||||||
// Clear line "Height xxx of xxx"
|
// Clear line "Height xxx of xxx"
|
||||||
std::cout << "\r \r";
|
std::cout << "\r \r";
|
||||||
|
@ -4372,6 +4396,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||||
if (is_init)
|
if (is_init)
|
||||||
print_accounts();
|
print_accounts();
|
||||||
show_balance_unlocked();
|
show_balance_unlocked();
|
||||||
|
on_refresh_finished(start_height, fetched_blocks, is_init, received_money);
|
||||||
}
|
}
|
||||||
catch (const tools::error::daemon_busy&)
|
catch (const tools::error::daemon_busy&)
|
||||||
{
|
{
|
||||||
|
@ -8134,13 +8159,13 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||||
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
|
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!m_wallet->is_trusted_daemon())
|
|
||||||
{
|
|
||||||
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCK_IDLE_SCOPE();
|
LOCK_IDLE_SCOPE();
|
||||||
|
key_images_sync_intern();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::key_images_sync_intern(){
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
|
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
|
||||||
|
@ -8149,19 +8174,23 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||||
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
|
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
|
||||||
if (height > 0)
|
if (height > 0)
|
||||||
{
|
{
|
||||||
success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
|
success_msg_writer() << tr("Key images synchronized to height ") << height;
|
||||||
<< print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
if (!m_wallet->is_trusted_daemon())
|
||||||
} else {
|
{
|
||||||
|
message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
success_msg_writer() << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
fail_msg_writer() << tr("Failed to import key images");
|
fail_msg_writer() << tr("Failed to import key images");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
|
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
|
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
|
||||||
|
|
|
@ -241,6 +241,8 @@ namespace cryptonote
|
||||||
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
|
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
|
||||||
std::string get_prompt() const;
|
std::string get_prompt() const;
|
||||||
bool print_seed(bool encrypted);
|
bool print_seed(bool encrypted);
|
||||||
|
void key_images_sync_intern();
|
||||||
|
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
|
||||||
|
|
||||||
struct transfer_view
|
struct transfer_view
|
||||||
{
|
{
|
||||||
|
|
|
@ -892,7 +892,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||||
m_ringdb(),
|
m_ringdb(),
|
||||||
m_last_block_reward(0),
|
m_last_block_reward(0),
|
||||||
m_encrypt_keys_after_refresh(boost::none),
|
m_encrypt_keys_after_refresh(boost::none),
|
||||||
m_unattended(unattended)
|
m_unattended(unattended),
|
||||||
|
m_device_last_key_image_sync(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3009,6 +3010,7 @@ bool wallet2::clear()
|
||||||
m_subaddresses.clear();
|
m_subaddresses.clear();
|
||||||
m_subaddress_labels.clear();
|
m_subaddress_labels.clear();
|
||||||
m_multisig_rounds_passed = 0;
|
m_multisig_rounds_passed = 0;
|
||||||
|
m_device_last_key_image_sync = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9235,9 +9237,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||||
auto & hwdev = get_account().get_device();
|
auto & hwdev = get_account().get_device();
|
||||||
if (!hwdev.has_ki_cold_sync()){
|
CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
|
||||||
throw std::invalid_argument("Device does not support cold ki sync protocol");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
||||||
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
||||||
|
@ -9248,7 +9248,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||||
|
|
||||||
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
|
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
|
||||||
|
|
||||||
return import_key_images(ski, 0, spent, unspent);
|
// Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
|
||||||
|
uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
|
||||||
|
m_device_last_key_image_sync = time(NULL);
|
||||||
|
|
||||||
|
return import_res;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
|
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
|
||||||
|
|
|
@ -810,6 +810,7 @@ namespace tools
|
||||||
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
||||||
|
|
||||||
uint64_t get_last_block_reward() const { return m_last_block_reward; }
|
uint64_t get_last_block_reward() const { return m_last_block_reward; }
|
||||||
|
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }
|
||||||
|
|
||||||
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)
|
||||||
|
@ -918,6 +919,9 @@ namespace tools
|
||||||
if(ver < 26)
|
if(ver < 26)
|
||||||
return;
|
return;
|
||||||
a & m_tx_device;
|
a & m_tx_device;
|
||||||
|
if(ver < 27)
|
||||||
|
return;
|
||||||
|
a & m_device_last_key_image_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1388,6 +1392,7 @@ namespace tools
|
||||||
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
||||||
std::string m_device_name;
|
std::string m_device_name;
|
||||||
std::string m_device_derivation_path;
|
std::string m_device_derivation_path;
|
||||||
|
uint64_t m_device_last_key_image_sync;
|
||||||
|
|
||||||
// Aux transaction data from device
|
// Aux transaction data from device
|
||||||
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
||||||
|
@ -1424,7 +1429,7 @@ namespace tools
|
||||||
std::unique_ptr<wallet_device_callback> m_device_callback;
|
std::unique_ptr<wallet_device_callback> m_device_callback;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 26)
|
BOOST_CLASS_VERSION(tools::wallet2, 27)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||||
|
|
Loading…
Reference in New Issue