diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index 6c2a287d..1a02c7c8 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -77,6 +77,32 @@ namespace epee namespace misc_utils { + template + struct triple + { // store a pair of values + typedef _Ty1 first_type; + typedef _Ty2 second_type; + typedef _Ty3 third_type; + + triple() + : first(), second(), third() + { // default construct + } + + triple(const _Ty1& _Val1, const _Ty2& _Val2, const _Ty3& _Val3) + : first(_Val1), second(_Val2), third(_Val3) + { // construct from specified values + } + + _Ty1 first; // the first stored value + _Ty2 second; // the second stored value + _Ty3 third; // the second stored value + }; + + + template t_type get_max_t_val(t_type t) { diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index e59a8f9d..0ae47e21 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3112,7 +3112,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height)const +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height, bool request_coinbase_info)const { CRITICAL_REGION_LOCAL(m_read_lock); if (!find_blockchain_supplement(qblock_ids, start_height)) @@ -3129,6 +3129,8 @@ bool blockchain_storage::find_blockchain_supplement(const std::list mis; get_transactions_direct(m_db_blocks[i]->bl.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, block " << get_block_hash(m_db_blocks[i]->bl) << " [" << i << "] contains missing transactions: " << mis); + if(request_coinbase_info) + blocks.back().third = m_db_transactions.find(get_transaction_hash(m_db_blocks[i]->bl.miner_tx)); } return true; } diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 73c9af2b..5f83c509 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -168,7 +168,7 @@ namespace currency typedef tools::db::basic_key_to_array_accessor outputs_container; // out_amount => ['global_output', ...] typedef tools::db::cached_key_value_accessor key_images_container; - typedef std::list, std::list > > > blocks_direct_container; + typedef std::list, std::list >, std::shared_ptr > > blocks_direct_container; friend struct add_transaction_input_visitor; //--------------------------------------------------------------------------------- @@ -255,7 +255,7 @@ namespace currency bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp)const; bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset)const; bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0, bool need_global_indexes = false)const; - bool find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0)const; + bool find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0, bool request_coinbase_info = false)const; //bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count)const; bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)const; bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index d51b7821..db87f721 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1739,12 +1739,24 @@ namespace currency bool r = currency::parse_and_validate_block_from_blob(bl_entry.block, blextin_ptr->bl); bdde.block_ptr = blextin_ptr; CHECK_AND_ASSERT_MES(r, false, "failed to parse block from blob: " << string_tools::buff_to_hex_nodelimer(bl_entry.block)); + size_t i = 0; + if (bl_entry.tx_global_outs.size()) + { + CHECK_AND_ASSERT_MES(bl_entry.tx_global_outs.size() == bl_entry.txs.size(), false, "tx_global_outs count " << bl_entry.tx_global_outs.size() << " count missmatch with bl_entry.txs count " << bl_entry.txs.size()); + } + for (const auto& tx_blob : bl_entry.txs) { std::shared_ptr tche_ptr(new currency::transaction_chain_entry()); r = parse_and_validate_tx_from_blob(tx_blob, tche_ptr->tx); CHECK_AND_ASSERT_MES(r, false, "failed to parse tx from blob: " << string_tools::buff_to_hex_nodelimer(tx_blob)); bdde.txs_ptr.push_back(tche_ptr); + if (bl_entry.tx_global_outs.size()) + { + CHECK_AND_ASSERT_MES(bl_entry.tx_global_outs[i].v.size() == tche_ptr->tx.vout.size(), false, "tx_global_outs for tx" << bl_entry.tx_global_outs[i].v.size() << " count missmatch with tche_ptr->tx.vout.size() count " << tche_ptr->tx.vout.size()); + tche_ptr->m_global_output_indexes = bl_entry.tx_global_outs[i].v; + } + i++; } } return true; diff --git a/src/currency_protocol/currency_protocol_defs.h b/src/currency_protocol/currency_protocol_defs.h index 0bb974e0..65e4a5b7 100644 --- a/src/currency_protocol/currency_protocol_defs.h +++ b/src/currency_protocol/currency_protocol_defs.h @@ -41,6 +41,7 @@ namespace currency struct block_direct_data_entry { std::shared_ptr block_ptr; + std::shared_ptr coinbase_ptr; std::list > txs_ptr; }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index ae353bc2..e98918a4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -279,7 +279,7 @@ namespace currency } blockchain_storage::blocks_direct_container bs; - if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height)) + if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height, req.need_global_indexes)) { res.status = API_RETURN_CODE_FAIL; return false; @@ -290,6 +290,7 @@ namespace currency res.blocks.resize(res.blocks.size()+1); res.blocks.back().block_ptr = b.first; res.blocks.back().txs_ptr = std::move(b.second); + res.blocks.back().coinbase_ptr = b.third; } res.status = API_RETURN_CODE_OK; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 687839e0..00fa9f7c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -239,7 +239,24 @@ size_t wallet2::scan_for_transaction_entries(const crypto::hash& tx_id, const cr return details.size(); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b) +void wallet2::fetch_tx_global_indixes(const currency::transaction& tx, std::vector& goutputs_indexes) +{ + currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); + req.txid = get_transaction_hash(tx); + bool r = m_core_proxy->call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES(req, res); + THROW_IF_TRUE_WALLET_EX(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); + THROW_IF_TRUE_WALLET_EX(res.status == API_RETURN_CODE_BUSY, error::daemon_busy, "get_o_indexes.bin"); + THROW_IF_TRUE_WALLET_EX(res.status != API_RETURN_CODE_OK, error::get_out_indices_error, res.status); + THROW_IF_TRUE_WALLET_EX(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error, + "transactions outputs size=" + std::to_string(tx.vout.size()) + + " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); + + goutputs_indexes = res.o_indexes; +} + +//---------------------------------------------------------------------------------------------------- +void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) { std::vector recipients, recipients_aliases; process_unconfirmed(tx, recipients, recipients_aliases); @@ -365,20 +382,23 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t pwallet_info->m_block_height = height; pwallet_info->m_block_timestamp = b.timestamp; + if (is_auditable()) + { + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pglobal_indexes && pglobal_indexes->size() == tx.vout.size(), "wrong pglobal_indexes = " << pglobal_indexes << ""); + } + std::vector outputs_index_local; + + if (!pglobal_indexes) + { #ifndef MOBILE_WALLET_BUILD - //good news - got money! take care about it - //usually we have only one transfer for user in transaction - currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); - currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); - req.txid = get_transaction_hash(tx); - bool r = m_core_proxy->call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES(req, res); - THROW_IF_TRUE_WALLET_EX(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); - THROW_IF_TRUE_WALLET_EX(res.status == API_RETURN_CODE_BUSY, error::daemon_busy, "get_o_indexes.bin"); - THROW_IF_TRUE_WALLET_EX(res.status != API_RETURN_CODE_OK, error::get_out_indices_error, res.status); - THROW_IF_TRUE_WALLET_EX(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); + //good news - got money! take care about it + //usually we have only one transfer for user in transaction + fetch_tx_global_indixes(tx, outputs_index_local); + pglobal_indexes = &outputs_index_local; #endif + } + + for (size_t i_in_outs = 0; i_in_outs != outs.size(); i_in_outs++) { @@ -458,9 +478,13 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t td.m_internal_output_index = o; td.m_key_image = ki; #ifdef MOBILE_WALLET_BUILD - td.m_global_output_index = WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED; + if (pglobal_indexes && pglobal_indexes->size() > o) + td.m_global_output_index = (*pglobal_indexes)[o]; + else + td.m_global_output_index = WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED; #else - td.m_global_output_index = res.o_indexes[o]; + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pglobal_indexes, "pglobal_indexes IS NULL in non mobile wallet"); + td.m_global_output_index = (*pglobal_indexes)[o]; #endif if (coin_base_tx) @@ -477,9 +501,13 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t add_transfer_to_transfers_cache(tx.vout[o].amount, transfer_index); uint64_t amount = tx.vout[o].amount; - auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index); - WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << amount << ", gindex " << td.m_global_output_index << " already exists"); - m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index; + if (is_watch_only() && is_auditable()) + { + WLT_CHECK_AND_ASSERT_MES_NO_RET(td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED, "td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED validation failed"); + auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index); + WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << amount << ", gindex " << td.m_global_output_index << " already exists"); + m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index; + } if (max_out_unlock_time < get_tx_unlock_time(tx, o)) max_out_unlock_time = get_tx_unlock_time(tx, o); @@ -1161,16 +1189,22 @@ void wallet2::process_new_blockchain_entry(const currency::block& b, const curre "current_index=" + std::to_string(height) + ", get_blockchain_current_height()=" + std::to_string(get_blockchain_current_size())); //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup + const std::vector* pglobal_index = nullptr; if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime()) { + pglobal_index = nullptr; + if (bche.coinbase_ptr.get()) + { + pglobal_index = &(bche.coinbase_ptr->m_global_output_indexes); + } TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(b.miner_tx, height, b); + process_new_transaction(b.miner_tx, height, b, pglobal_index); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); for(const auto& tx_entry: bche.txs_ptr) { - process_new_transaction(tx_entry->tx, height, b); + process_new_transaction(tx_entry->tx, height, b, &(tx_entry->m_global_output_indexes)); } TIME_MEASURE_FINISH(txs_handle_time); WLT_LOG_L3("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -1245,6 +1279,9 @@ void wallet2::pull_blocks(size_t& blocks_added, std::atomic& stop) currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response res = AUTO_VAL_INIT(res); req.minimum_height = get_wallet_minimum_height(); + if (is_auditable()) + req.need_global_indexes = true; + m_chain.get_short_chain_history(req.block_ids); bool r = m_core_proxy->call_COMMAND_RPC_GET_BLOCKS_DIRECT(req, res); if (!r) @@ -2865,6 +2902,7 @@ bool wallet2::get_pos_entries(currency::COMMAND_RPC_SCAN_POS::request& req) { for (size_t i = 0; i != m_transfers.size(); i++) { + WLT_CHECK_AND_ASSERT_MES(tr.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED, false, "Wrong output input in transaction"); auto& tr = m_transfers[i]; uint64_t stake_unlock_time = 0; if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e02f7138..98306f11 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -830,7 +830,8 @@ private: void add_transfers_to_expiration_list(const std::vector& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id); void remove_transfer_from_expiration_list(uint64_t transfer_index); void load_keys(const std::string& keys_file_name, const std::string& password, uint64_t file_signature); - void process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b); + void process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes); + void fetch_tx_global_indixes(const currency::transaction& tx, std::vector& goutputs_indexes); void detach_blockchain(uint64_t including_height); bool extract_offers_from_transfer_entry(size_t i, std::unordered_map& offers_local); bool select_my_offers(std::list& offers);