From 9e82b694da120708652871b55f639d1ef306a7ec Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 May 2016 21:48:18 +0100 Subject: remove original Cryptonote blockchain_storage blockchain format --- src/rpc/core_rpc_server.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/rpc/core_rpc_server.cpp') diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index e6fc11060..c15a647de 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1023,7 +1023,6 @@ namespace cryptonote return false; } -#if BLOCKCHAIN_DB == DB_LMDB const Blockchain &blockchain = m_core.get_blockchain_storage(); uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version(); res.version = blockchain.get_current_hard_fork_version(); @@ -1031,11 +1030,6 @@ namespace cryptonote res.state = blockchain.get_hard_fork_state(); res.status = CORE_RPC_STATUS_OK; return true; -#else - error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC; - error_resp.message = "Hard fork inoperative in memory mode."; - return false; -#endif } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp) -- cgit v1.2.3 From 4258dab4d6dcbf563daa054c7e05e8a0027290b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 5 Jun 2016 10:46:18 +0100 Subject: core: new /getrandom_rctouts.bin binary RPC call to get random ringct outputs to mix with --- src/cryptonote_core/blockchain.cpp | 98 +++++++++++++++++++++++++++++++++ src/cryptonote_core/blockchain.h | 25 +++++++++ src/cryptonote_core/cryptonote_core.cpp | 5 ++ src/cryptonote_core/cryptonote_core.h | 8 +++ src/rpc/core_rpc_server.cpp | 24 ++++++++ src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 30 ++++++++++ 7 files changed, 192 insertions(+) (limited to 'src/rpc/core_rpc_server.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cc6b48b6b..46ff7efbb 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1597,6 +1597,104 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT return true; } //------------------------------------------------------------------ +// This function adds the ringct output at index i to the list +// unlocked and other such checks should be done by here. +void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, size_t i) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry()); + oen.global_amount_index = i; + output_data_t data = m_db->get_output_key(0, i); + oen.out_key = data.pubkey; + oen.commitment = m_db->get_rct_commitment(i); +} +//------------------------------------------------------------------ +// This function takes an RPC request for mixins and creates an RPC response +// with the requested mixins. +// TODO: figure out why this returns boolean / if we should be returning false +// in some cases +bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // for each amount that we need to get mixins for, get random outputs + // from BlockchainDB where is req.outs_count (number of mixins). + auto num_outs = m_db->get_num_outputs(0); + // ensure we don't include outputs that aren't yet eligible to be used + // outpouts are sorted by height + while (num_outs > 0) + { + const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1); + const uint64_t height = m_db->get_tx_block_height(toi.first); + if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) + break; + --num_outs; + } + + std::unordered_set seen_indices; + + // if there aren't enough outputs to mix with (or just enough), + // use all of them. Eventually this should become impossible. + if (num_outs <= req.outs_count) + { + for (uint64_t i = 0; i < num_outs; i++) + { + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if tx is unlocked, add output to result_outs + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + else + { + // while we still need more mixins + while (res.outs.size() < req.outs_count) + { + // if we've gone through every possible output, we've gotten all we can + if (seen_indices.size() == num_outs) + { + break; + } + + // get a random output index from the DB. If we've already seen it, + // return to the top of the loop and try again, otherwise add it to the + // list of output indices we've seen. + + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + uint64_t i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after sqrt + if (i == num_outs) + --i; + + if (seen_indices.count(i)) + { + continue; + } + seen_indices.emplace(i); + + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if the output's transaction is unlocked, add the output's index to + // our list. + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + return true; +} +//------------------------------------------------------------------ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 1524693b8..701c17d9e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -454,6 +454,23 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * @brief gets random ringct outputs to mix with + * + * This function takes an RPC request for outputs to mix with + * and creates an RPC response with the resultant output indices + * and the matching keys. + * + * Outputs to mix with are randomly selected from the utxo set + * for each output amount in the request. + * + * @param req the output amounts and number of mixins to select + * @param res return-by-reference the resultant output indices + * + * @return true + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @brief gets the global indices for outputs from a given transaction * @@ -1053,6 +1070,14 @@ namespace cryptonote */ void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const; + /** + * @brief adds the given output to the requested set of random ringct outputs + * + * @param outs return-by-reference the set the output is to be added to + * @param i the rct output index + */ + void add_out_to_get_rct_random_outs(std::list& outs, size_t i) const; + /** * @brief checks if a transaction is unlocked (its outputs spendable) * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 550c55759..b3a7cb98d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -706,6 +706,11 @@ namespace cryptonote return m_blockchain_storage.get_outs(req, res); } //----------------------------------------------------------------------------------------------- + bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const + { + return m_blockchain_storage.get_random_rct_outs(req, res); + } + //----------------------------------------------------------------------------------------------- bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const { return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 84b1f27a8..d16bd6553 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -479,6 +479,14 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * + * @copydoc Blockchain::get_random_rct_outs + * + * @note see Blockchain::get_random_rct_outs + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @copydoc miner::pause diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c15a647de..586b09179 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -250,6 +250,30 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) + { + CHECK_CORE_BUSY(); + res.status = "Failed"; + if(!m_core.get_random_rct_outs(req, res)) + { + return true; + } + + res.status = CORE_RPC_STATUS_OK; + std::stringstream ss; + typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry; + CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty"); + std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_BUSY(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 6ebf41abc..9fcf3abf8 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -79,6 +79,7 @@ namespace cryptonote MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT) MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) @@ -128,6 +129,7 @@ namespace cryptonote bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); + bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 22c3590e1..bbea145e5 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -315,6 +315,36 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS + { + struct request + { + uint64_t outs_count; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs_count) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct out_entry + { + uint64_t global_amount_index; + crypto::public_key out_key; + rct::key commitment; + }; +#pragma pack(pop) + + struct response + { + std::list outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX { -- cgit v1.2.3 From c2ec6d35b9a8130fe1253d4933741506fee504cf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Jul 2016 10:02:12 +0100 Subject: mixable transactions must be rct for v3 --- src/cryptonote_core/blockchain.cpp | 34 ++++++++++++++++++++++++------ src/cryptonote_core/verification_context.h | 1 + src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 2 ++ 4 files changed, 33 insertions(+), 6 deletions(-) (limited to 'src/rpc/core_rpc_server.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f46de9ca8..bef880627 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2262,17 +2262,39 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context if (txin.type() == typeid(txin_to_key)) { const txin_to_key& in_to_key = boost::get(txin); - uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); - LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); - // n_outputs includes the output we're considering - if (n_outputs <= 2) - ++n_unmixable; - else + if (in_to_key.amount == 0) + { + // always consider rct inputs mixable. Even if there's not enough rct + // inputs on the chain to mix with, this is going to be the case for + // just a few blocks right after the fork at most ++n_mixable; + } + else + { + uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); + LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); + // n_outputs includes the output we're considering + if (n_outputs <= 2) + ++n_unmixable; + else + ++n_mixable; + } if (in_to_key.key_offsets.size() - 1 < mixin) mixin = in_to_key.key_offsets.size() - 1; } } + + // for v3, we force txes with all mixable inputs to be rct + if (m_hardfork->get_current_version() >= 3) + { + if (n_unmixable == 0 && tx.version == 1) + { + LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " is not rct and does not have unmixable inputs"); + tvc.m_not_rct = true; + return false; + } + } + if (mixin < 2) { if (n_unmixable == 0) diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index e58291ea9..0bb635e84 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -47,6 +47,7 @@ namespace cryptonote bool m_too_big; bool m_overspend; bool m_fee_too_low; + bool m_not_rct; }; struct block_verification_context diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 586b09179..cef53b5ce 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -483,6 +483,8 @@ namespace cryptonote res.reason = "overspend"; if ((res.fee_too_low = tvc.m_fee_too_low)) res.reason = "fee too low"; + if ((res.not_rct = tvc.m_not_rct)) + res.reason = "tx is not ringct"; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 5dd9de8d5..780de8682 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -376,6 +376,7 @@ namespace cryptonote bool too_big; bool overspend; bool fee_too_low; + bool not_rct; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -388,6 +389,7 @@ namespace cryptonote KV_SERIALIZE(too_big) KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) + KV_SERIALIZE(not_rct) END_KV_SERIALIZE_MAP() }; }; -- cgit v1.2.3 From 3cb2edec6623e1a6b10d67c7335b55ccb410b336 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 13 Jul 2016 19:26:11 +0100 Subject: rpc: send global indices along with blocks/transacions on refresh This plugs a privacy leak, where the wallet tells the daemon which transactions contain outputs for the wallet by asking for additional information for that particular transaction. As a nice bonus, this actually makes refresh slightly faster. --- src/rpc/core_rpc_server.cpp | 16 ++++++++ src/rpc/core_rpc_server_commands_defs.h | 20 ++++++++++ src/wallet/wallet2.cpp | 67 ++++++++++++++++++--------------- src/wallet/wallet2.h | 10 ++--- 4 files changed, 78 insertions(+), 35 deletions(-) (limited to 'src/rpc/core_rpc_server.cpp') diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index cef53b5ce..5aa3591ab 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -159,9 +159,25 @@ namespace cryptonote { res.blocks.resize(res.blocks.size()+1); res.blocks.back().block = block_to_blob(b.first); + res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.first.miner_tx), res.output_indices.back().indices.back().indices); + if (!r) + { + res.status = "Failed"; + return false; + } + size_t txidx = 0; BOOST_FOREACH(auto& t, b.second) { res.blocks.back().txs.push_back(tx_to_blob(t)); + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); + bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); + if (!r) + { + res.status = "Failed"; + return false; + } } } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 780de8682..5f0608532 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -76,18 +76,38 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; + struct tx_output_indices + { + std::vector indices; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(indices) + END_KV_SERIALIZE_MAP() + }; + + struct block_output_indices + { + std::vector indices; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(indices) + END_KV_SERIALIZE_MAP() + }; + struct response { std::list blocks; uint64_t start_height; uint64_t current_height; std::string status; + std::vector output_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) KV_SERIALIZE(status) + KV_SERIALIZE(output_indices) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7486d3282..440aec42c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -201,7 +201,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const rct::key & sk, unsigned return rct::decodeRct(rv, sk, i, mask); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool) +void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { if (!miner_tx) process_unconfirmed(tx, height); @@ -388,20 +388,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ { //good news - got money! take care about it //usually we have only one transfer for user in transaction - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); if (!pool) { - req.txid = get_transaction_hash(tx); - m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status); - THROW_WALLET_EXCEPTION_IF(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())); + THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, + "transactions outputs size=" + std::to_string(tx.vout.size()) + + " not match with daemon response size=" + std::to_string(o_indices.size())); } BOOST_FOREACH(size_t o, outs) @@ -422,7 +413,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ transfer_details& td = m_transfers.back(); td.m_block_height = height; td.m_internal_output_index = o; - td.m_global_output_index = res.o_indexes[o]; + td.m_global_output_index = o_indices[o]; td.m_tx = tx; td.m_key_image = ki[o]; td.m_amount = tx.vout[o].amount; @@ -462,7 +453,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ transfer_details &td = m_transfers[kit->second]; td.m_block_height = height; td.m_internal_output_index = o; - td.m_global_output_index = res.o_indexes[o]; + td.m_global_output_index = o_indices[o]; td.m_tx = tx; td.m_amount = tx.vout[o].amount; if (td.m_amount == 0) @@ -613,15 +604,20 @@ void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t heigh entry.first->second.m_timestamp = ts; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height) +void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices) { + size_t txidx = 0; + THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != o_indices.indices.size(), error::wallet_internal_error, + "block transactions=" + std::to_string(bche.txs.size()) + + " not match with daemon response size=" + std::to_string(o_indices.indices.size())); + //handle transactions from new block //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 if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(b.miner_tx, height, b.timestamp, true, false); + process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); @@ -630,7 +626,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry cryptonote::transaction tx; bool r = parse_and_validate_tx_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(tx, height, b.timestamp, false, false); + process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); } TIME_MEASURE_FINISH(txs_handle_time); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -680,7 +676,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl bl_id = get_block_hash(bl); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &blocks) +void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); @@ -693,9 +689,13 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); + THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, + "mismatched blocks (" + boost::lexical_cast(res.blocks.size()) + ") and output_indices (" + + boost::lexical_cast(res.output_indices.size()) + ") sizes from daemon"); blocks_start_height = res.start_height; blocks = res.blocks; + o_indices = res.output_indices; } //---------------------------------------------------------------------------------------------------- void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &hashes) @@ -716,10 +716,13 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, hashes = res.m_block_ids; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_blocks(uint64_t start_height, const std::list &blocks, uint64_t& blocks_added) +void wallet2::process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added) { size_t current_index = start_height; blocks_added = 0; + size_t tx_o_indices_idx = 0; + + THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch"); int threads = tools::get_max_concurrency(); if (threads > 1) @@ -762,7 +765,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list= m_blockchain.size()) { - process_new_blockchain_entry(bl, *blocki, bl_id, current_index); + process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -774,7 +777,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list= m_blockchain.size()) { - process_new_blockchain_entry(bl, bl_entry, bl_id, current_index); + process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -808,7 +811,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, bool &error) +void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error) { error = false; @@ -850,7 +854,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei } // pull the new blocks - pull_blocks(start_height, blocks_start_height, short_chain_history, blocks); + pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices); } catch(...) { @@ -990,7 +994,7 @@ void wallet2::update_pool_state() { if (tx_hash == txid) { - process_new_transaction(tx, 0, time(NULL), false, true); + process_new_transaction(tx, std::vector(), 0, time(NULL), false, true); } else { @@ -1106,6 +1110,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re boost::thread pull_thread; uint64_t blocks_start_height; std::list blocks; + std::vector o_indices; // pull the first set of blocks get_short_chain_history(short_chain_history); @@ -1122,7 +1127,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // and then fall through to regular refresh processing } - pull_blocks(start_height, blocks_start_height, short_chain_history, blocks); + pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices); // always reset start_height to 0 to force short_chain_ history to be used on // subsequent pulls in this refresh. start_height = 0; @@ -1134,10 +1139,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // pull the next set of blocks while we're processing the current one uint64_t next_blocks_start_height; std::list next_blocks; + std::vector next_o_indices; bool error = false; - pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, error);}); + pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);}); - process_blocks(blocks_start_height, blocks, added_blocks); + process_blocks(blocks_start_height, blocks, o_indices, added_blocks); blocks_fetched += added_blocks; pull_thread.join(); if(!added_blocks) @@ -1146,6 +1152,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // switch to the new blocks from the daemon blocks_start_height = next_blocks_start_height; blocks = next_blocks; + o_indices = next_o_indices; // handle error from async fetching thread if (error) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 4bb882d08..2631f630b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -432,17 +432,17 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const std::string& password); - void process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool); - void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height); + void process_new_transaction(const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); + void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; bool clear(); - void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &blocks); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); - void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, bool &error); - void process_blocks(uint64_t start_height, const std::list &blocks, uint64_t& blocks_added); + void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error); + void process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::list& selected_transfers, bool trusted_daemon); bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height); -- cgit v1.2.3