diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bde08383f..f39b4165f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7946,12 +7946,13 @@ bool simple_wallet::show_transfers(const std::vector &args_) } } - auto formatter = boost::format("%8.8llu %6.6s %8.8s %16.16s %20.20s %s %s %14.14s %s %s - %s"); + auto formatter = boost::format("%8.8llu %6.6s %8.8s %12.12s %16.16s %20.20s %s %s %14.14s %s %s - %s"); message_writer(color, false) << formatter % transfer.block % tools::pay_type_string(transfer.type) % transfer.lock_msg + % (transfer.checkpointed ? "checkpointed" : "no") % tools::get_human_readable_timestamp(transfer.timestamp) % print_money(transfer.amount) % string_tools::pod_to_hex(transfer.hash) @@ -9265,6 +9266,7 @@ bool simple_wallet::show_transfer(const std::vector &args) else success_msg_writer() << "locked for " << tools::get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold)); } + success_msg_writer() << "Checkpointed: " << (pd.m_block_height <= m_wallet->get_immutable_height() ? "Yes" : "No"); success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor; success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid); return true; diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 414c9f657..d4c1fd637 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -58,6 +58,7 @@ void NodeRPCProxy::invalidate() m_contributed_service_nodes.clear(); m_height = 0; + m_immutable_height = 0; for (size_t n = 0; n < 256; ++n) m_earliest_height[n] = 0; m_dynamic_base_fee_estimate = {0, 0}; @@ -93,6 +94,8 @@ boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version void NodeRPCProxy::set_height(uint64_t h) { m_height = h; + if (h < m_immutable_height) + m_immutable_height = 0; } boost::optional NodeRPCProxy::get_info() const @@ -115,6 +118,7 @@ boost::optional NodeRPCProxy::get_info() const m_height = resp_t.height; m_target_height = resp_t.target_height; m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit; + m_immutable_height = resp_t.immutable_height; m_get_info_time = now; } return boost::optional(); @@ -138,6 +142,15 @@ boost::optional NodeRPCProxy::get_target_height(uint64_t &height) c return boost::optional(); } +boost::optional NodeRPCProxy::get_immutable_height(uint64_t &height) const +{ + auto res = get_info(); + if (res) + return res; + height = m_immutable_height; + return boost::optional(); +} + boost::optional NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const { auto res = get_info(); diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index c9df170f8..3e7f6ffa4 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -49,6 +49,7 @@ public: boost::optional get_height(uint64_t &height) const; void set_height(uint64_t h); boost::optional get_target_height(uint64_t &height) const; + boost::optional get_immutable_height(uint64_t &height) const; boost::optional get_block_weight_limit(uint64_t &block_weight_limit) const; boost::optional get_earliest_height(uint8_t version, uint64_t &earliest_height) const; boost::optional get_dynamic_base_fee_estimate(uint64_t grace_blocks, cryptonote::byte_and_output_fees &fees) const; @@ -80,6 +81,7 @@ private: mutable std::vector m_contributed_service_nodes; mutable uint64_t m_height; + mutable uint64_t m_immutable_height; mutable uint64_t m_earliest_height[256]; mutable cryptonote::byte_and_output_fees m_dynamic_base_fee_estimate; mutable uint64_t m_dynamic_base_fee_estimate_cached_height; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 80ab905b7..38df49a4a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3293,6 +3293,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if(last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash)) received_money = true; + uint64_t immutable_height = 0; + boost::optional fail_string = m_node_rpc_proxy.get_immutable_height(immutable_height); + if (!fail_string) + m_immutable_height = immutable_height; + try { // If stop() is called we don't need to check pending transactions @@ -5801,6 +5806,7 @@ void wallet2::fill_transfer_view(wallet2::transfer_view &entry, const crypto::ha const bool unlocked = is_transfer_unlocked(entry.unlock_time, entry.height); entry.lock_msg = unlocked ? "unlocked" : "locked"; set_confirmations(entry, get_blockchain_current_height(), get_last_block_reward()); + entry.checkpointed = entry.height <= m_immutable_height; } //------------------------------------------------------------------------------------------------------------------------------ void wallet2::fill_transfer_view(wallet2::transfer_view &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd) const @@ -5832,6 +5838,7 @@ void wallet2::fill_transfer_view(wallet2::transfer_view &entry, const crypto::ha entry.subaddr_indices.push_back({pd.m_subaddr_account, i}); entry.address = get_subaddress_as_str({pd.m_subaddr_account, 0}); entry.confirmed = true; + entry.checkpointed = entry.height <= m_immutable_height; set_confirmations(entry, get_blockchain_current_height(), get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ @@ -5966,12 +5973,12 @@ void wallet2::get_transfers(get_transfers_args_t args, std::vector& transfers, bool formatting) const { uint64_t running_balance = 0; - auto data_formatter = boost::format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,'%s',%s"); - auto title_formatter = boost::format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s"); + auto data_formatter = boost::format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,'%s',%s"); + auto title_formatter = boost::format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s"); if (formatting) { - title_formatter = boost::format("%8.8s,%9.9s,%8.8s,%16.16s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s"); - data_formatter = boost::format("%8.8s,%9.9s,%8.8s,%16.16s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s"); + title_formatter = boost::format("%8.8s,%9.9s,%8.8s,%14.14s,%16.16s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s"); + data_formatter = boost::format("%8.8s,%9.9s,%8.8s,%14.14s,%16.16s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s"); } auto new_line = [&](std::stringstream& output){ @@ -5990,6 +5997,7 @@ std::string wallet2::transfers_to_csv(const std::vector& % tr("block") % tr("type") % tr("lock") + % tr("checkpointed") % tr("timestamp") % tr("amount") % tr("running balance") @@ -6027,6 +6035,7 @@ std::string wallet2::transfers_to_csv(const std::vector& % transfer.block % pay_type_string(transfer.type) % transfer.lock_msg + % (transfer.checkpointed ? "checkpointed" : "no") % tools::get_human_readable_timestamp(transfer.timestamp) % cryptonote::print_money(transfer.amount) % cryptonote::print_money(running_balance) @@ -6056,6 +6065,7 @@ std::string wallet2::transfers_to_csv(const std::vector& % "" % "" % "" + % "" % it->address % cryptonote::print_money(it->amount) % "" diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9929bc1f4..7d84d8d5f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -494,6 +494,7 @@ private: bool double_spend_seen; // True if the key image(s) for the transfer have been seen before. uint64_t confirmations; // Number of block mined since the block containing this transaction (or block height at which the transaction should be added to a block if not yet confirmed). uint64_t suggested_confirmations_threshold; + bool checkpointed = false; // If the transfer is backed by atleast (2 service node|| 1 hardcoded) checkpoints // TODO(loki): Make this count the number of checkpoints backing this transfer }; typedef std::vector transfer_container; @@ -963,6 +964,7 @@ private: 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; } + uint64_t get_immutable_height() const { return m_immutable_height; } template inline void serialize(t_archive &a, const unsigned int ver) @@ -1077,6 +1079,9 @@ private: if(ver < 28) return; a & m_cold_key_images; + if(ver < 29) + return; + a & m_immutable_height; } /*! @@ -1667,6 +1672,7 @@ private: std::string m_device_derivation_path; uint64_t m_device_last_key_image_sync; bool m_offline; + uint64_t m_immutable_height; // Aux transaction data from device std::unordered_map m_tx_device; @@ -1721,7 +1727,7 @@ private: bool parse_priority (const std::string& arg, uint32_t& priority); } -BOOST_CLASS_VERSION(tools::wallet2, 28) +BOOST_CLASS_VERSION(tools::wallet2, 29) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) 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_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 736d168b8..58b60984c 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1499,6 +1499,7 @@ namespace wallet_rpc bool double_spend_seen; // True if the key image(s) for the transfer have been seen before. uint64_t confirmations; // Number of block mined since the block containing this transaction (or block height at which the transaction should be added to a block if not yet confirmed). uint64_t suggested_confirmations_threshold; // Estimation of the confirmations needed for the transaction to be included in a block. + uint64_t checkpointed; // If transfer is backed by atleast 2 Service Node Checkpoints, 0 if it is not, see immutable_height in the daemon rpc call get_info BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -1517,6 +1518,7 @@ namespace wallet_rpc KV_SERIALIZE(double_spend_seen) KV_SERIALIZE_OPT(confirmations, (uint64_t)0) KV_SERIALIZE_OPT(suggested_confirmations_threshold, (uint64_t)0) + KV_SERIALIZE(checkpointed) END_KV_SERIALIZE_MAP() };