From dcbc17e97e782d168d1653e216d415bb4d34a892 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Jun 2018 13:06:41 +0100 Subject: [PATCH] wallet: include a suggested number of confirmations based on amount This is based on how much an attacking miner stands to lose in block rewardy by mining a private chain which double spends a payment. This is not foolproof, since mining is based on luck, and breaks down as the attacking miner nears 50% of the network hash rate, and the estimation is based on a constant block reward. --- src/simplewallet/simplewallet.cpp | 4 ++++ src/wallet/wallet2.cpp | 13 ++++++++++++- src/wallet/wallet2.h | 10 +++++++++- src/wallet/wallet_rpc_server.cpp | 19 +++++++++++++++++++ src/wallet/wallet_rpc_server_commands_defs.h | 6 +++++- 5 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 24c42a597..a64936e4f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7408,8 +7408,12 @@ bool simple_wallet::show_transfer(const std::vector &args) if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); + uint64_t last_block_reward = m_wallet->get_last_block_reward(); + uint64_t suggested_threshold = last_block_reward ? (pd.m_amount + last_block_reward - 1) / last_block_reward : 0; if (bh >= last_block_height) success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock"; + else if (suggested_threshold > 0) + success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations (" << suggested_threshold << " suggested threshold)"; else success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations"; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index aa6874d17..5b8f1738b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -686,7 +686,8 @@ wallet2::wallet2(network_type nettype, bool restricted): m_light_wallet_unlocked_balance(0), m_key_on_device(false), m_ring_history_saved(false), - m_ringdb() + m_ringdb(), + m_last_block_reward(0) { } @@ -1672,6 +1673,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]); } TIME_MEASURE_FINISH(txs_handle_time); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); }else { @@ -3166,6 +3168,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3224,6 +3227,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3319,6 +3323,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3371,6 +3376,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3417,6 +3423,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p m_subaddress_lookahead_major = 5; m_subaddress_lookahead_minor = 20; } + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) { store(); @@ -3513,6 +3520,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!m_wallet_file.empty()) @@ -4015,6 +4023,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass if (m_blockchain.empty()) { m_blockchain.push_back(genesis_hash); + m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); } else { @@ -4404,6 +4413,7 @@ void wallet2::rescan_blockchain(bool refresh) generate_genesis(genesis); crypto::hash genesis_hash = get_block_hash(genesis); m_blockchain.push_back(genesis_hash); + m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); add_subaddress_account(tr("Primary account")); if (refresh) @@ -10006,6 +10016,7 @@ void wallet2::import_blockchain(const std::tuple wallet2::export_outputs() const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2da6dd21a..ba5df9828 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -762,6 +762,9 @@ namespace tools void rescan_blockchain(bool refresh = true); bool is_transfer_unlocked(const transfer_details& td) 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; } + template inline void serialize(t_archive &a, const unsigned int ver) { @@ -863,6 +866,9 @@ namespace tools if(ver < 24) return; a & m_ring_history_saved; + if(ver < 25) + return; + a & m_last_block_reward; } /*! @@ -1298,9 +1304,11 @@ namespace tools bool m_ring_history_saved; std::unique_ptr m_ringdb; boost::optional m_ringdb_key; + + uint64_t m_last_block_reward; }; } -BOOST_CLASS_VERSION(tools::wallet2, 24) +BOOST_CLASS_VERSION(tools::wallet2, 25) 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 7f7d33642..2970968e4 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -74,6 +74,21 @@ namespace } return pwd_container; } + //------------------------------------------------------------------------------------------------------------------------------ + void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward) + { + if (entry.height >= blockchain_height) + { + entry.confirmations = 0; + entry.suggested_confirmations_threshold = 0; + return; + } + entry.confirmations = blockchain_height - entry.height; + if (block_reward == 0) + entry.suggested_confirmations_threshold = 0; + else + entry.suggested_confirmations_threshold = (entry.amount + block_reward - 1) / block_reward; + } } namespace tools @@ -258,6 +273,7 @@ namespace tools entry.type = "in"; entry.subaddr_index = pd.m_subaddr_index; entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd) @@ -284,6 +300,7 @@ namespace tools entry.type = "out"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd) @@ -303,6 +320,7 @@ namespace tools entry.type = is_failed ? "failed" : "pending"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd) @@ -322,6 +340,7 @@ namespace tools entry.type = "pool"; entry.subaddr_index = pd.m_subaddr_index; entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ 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) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 96e135f01..aa61640a3 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/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 0 +#define WALLET_RPC_VERSION_MINOR 1 #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 @@ -1194,6 +1194,8 @@ namespace wallet_rpc cryptonote::subaddress_index subaddr_index; std::string address; bool double_spend_seen; + uint64_t confirmations; + uint64_t suggested_confirmation_threshold; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -1209,6 +1211,8 @@ namespace wallet_rpc KV_SERIALIZE(subaddr_index); KV_SERIALIZE(address); KV_SERIALIZE(double_spend_seen) + KV_SERIALIZE_OPT(confirmations, 0) + KV_SERIALIZE_OPT(suggested_confirmation_threshold, 0) END_KV_SERIALIZE_MAP() };