diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 3 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 11 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 193 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 9 |
4 files changed, 154 insertions, 62 deletions
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 63c4ea3cc..603739598 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -55,7 +55,8 @@ TransactionHistoryImpl::TransactionHistoryImpl(WalletImpl *wallet) TransactionHistoryImpl::~TransactionHistoryImpl() { - + for (auto t : m_history) + delete t; } int TransactionHistoryImpl::count() const diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index d21d8b900..215b61aef 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -605,6 +605,17 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const break; } } + else if (has_payment_id) { + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_short); + bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + if (!r) { + m_status = Status_Error; + m_errorString = tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(payment_id_short); + break; + } + } + //std::vector<tools::wallet2::pending_tx> ptx_vector; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 15a134257..ea3994435 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -191,7 +191,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt } // Remove line breaks the user might have inserted - password.erase(std::remove(password.begin() - 1, password.end(), '\n'), password.end()); + password.erase(std::remove(password.end() - 1, password.end(), '\n'), password.end()); password.erase(std::remove(password.end() - 1, password.end(), '\r'), password.end()); return {tools::password_container(std::move(password))}; } @@ -1574,12 +1574,35 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re std::list<cryptonote::block_complete_entry> blocks; std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices; + std::string daemon_height_err = ""; + uint64_t daemon_bc_height = get_daemon_blockchain_height(daemon_height_err); + if(daemon_height_err.size() > 0) { + throw std::runtime_error(daemon_height_err); + } + // pull the first set of blocks get_short_chain_history(short_chain_history); m_run.store(true, std::memory_order_relaxed); if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) { - if (!start_height) - start_height = m_refresh_from_block_height; + + // even target_height can be zero if the daemon just started and hasn't gotten some sync + // data back from peers .. hmmm, what to do ... O.o (you can see him thinking) + // i'm going with infiniti loop until i get something bigger than zero or err ... moneromoo don't kill me + std::string daemon_target_err = ""; + uint64_t daemon_target_height = 0; + + while(daemon_target_height == 0) + { + daemon_target_height = get_daemon_blockchain_target_height(daemon_target_err); + if(daemon_target_err.size() > 0) { + daemon_target_height = get_approximate_blockchain_height(); // - x? + } + } + + if (m_refresh_from_block_height > daemon_target_height) m_refresh_from_block_height = daemon_target_height - 1; + if (!start_height) start_height = m_refresh_from_block_height; + if (start_height >= daemon_bc_height) start_height = daemon_bc_height - 1; + // we can shortcut by only pulling hashes up to the start_height fast_refresh(start_height, blocks_start_height, short_chain_history); // regenerate the history now that we've got a full set of hashes @@ -1589,53 +1612,56 @@ 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, 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; - - while(m_run.load(std::memory_order_relaxed)) + if(!(m_refresh_from_block_height >= daemon_bc_height)) { - try - { - // pull the next set of blocks while we're processing the current one - uint64_t next_blocks_start_height; - std::list<cryptonote::block_complete_entry> next_blocks; - std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> 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, next_o_indices, error);}); - - process_blocks(blocks_start_height, blocks, o_indices, added_blocks); - blocks_fetched += added_blocks; - pull_thread.join(); - if(!added_blocks) - break; - - // switch to the new blocks from the daemon - blocks_start_height = next_blocks_start_height; - blocks = next_blocks; - o_indices = next_o_indices; + 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; - // handle error from async fetching thread - if (error) - { - throw std::runtime_error("proxy exception in refresh thread"); - } - } - catch (const std::exception&) + while(m_run.load(std::memory_order_relaxed)) { - blocks_fetched += added_blocks; - if (pull_thread.joinable()) - pull_thread.join(); - if(try_count < 3) + try { - LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); - ++try_count; + // pull the next set of blocks while we're processing the current one + uint64_t next_blocks_start_height; + std::list<cryptonote::block_complete_entry> next_blocks; + std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> 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, next_o_indices, error);}); + + process_blocks(blocks_start_height, blocks, o_indices, added_blocks); + blocks_fetched += added_blocks; + pull_thread.join(); + if(!added_blocks) + break; + + // 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) + { + throw std::runtime_error("proxy exception in refresh thread"); + } } - else + catch (const std::exception&) { - LOG_ERROR("pull_blocks failed, try_count=" << try_count); - throw; + blocks_fetched += added_blocks; + if (pull_thread.joinable()) + pull_thread.join(); + if(try_count < 3) + { + LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); + ++try_count; + } + else + { + LOG_ERROR("pull_blocks failed, try_count=" << try_count); + throw; + } } } } @@ -2730,7 +2756,7 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo utd.m_amount_out = 0; for (const auto &d: dests) utd.m_amount_out += d.amount; - utd.m_amount_out += change_amount; + utd.m_amount_out += change_amount; // dests does not contain change utd.m_change = change_amount; utd.m_sent_time = time(NULL); utd.m_tx = (const cryptonote::transaction_prefix&)tx; @@ -3057,7 +3083,7 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s ptx.change_dts = sd.change_dts; ptx.selected_transfers = sd.selected_transfers; ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet - ptx.dests = sd.splitted_dsts; + ptx.dests = sd.dests; ptx.construction_data = sd; txs.push_back(ptx); @@ -3335,8 +3361,8 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count); // generate output indices to request - COMMAND_RPC_GET_OUTPUTS::request req = AUTO_VAL_INIT(req); - COMMAND_RPC_GET_OUTPUTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp); size_t num_selected_transfers = 0; for(size_t idx: selected_transfers) @@ -3442,7 +3468,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si // sort the subsection, to ensure the daemon doesn't know wich output is ours std::sort(req.outputs.begin() + start, req.outputs.end(), - [](const COMMAND_RPC_GET_OUTPUTS::out &a, const COMMAND_RPC_GET_OUTPUTS::out &b) { return a.index < b.index; }); + [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); } for (auto i: req.outputs) @@ -3656,6 +3682,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; + ptx.construction_data.dests = dsts; } void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, @@ -3776,6 +3803,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = true; + ptx.construction_data.dests = dsts; } static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) @@ -4688,6 +4716,53 @@ bool wallet2::verify(const std::string &data, const cryptonote::account_public_a return crypto::check_signature(hash, address.m_spend_public_key, s); } //---------------------------------------------------------------------------------------------------- +crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const +{ + std::vector<tx_extra_field> tx_extra_fields; + if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + } + + // Due to a previous bug, there might be more than one tx pubkey in extra, one being + // the result of a previously discarded signature. + // For speed, since scanning for outputs is a slow process, we check whether extra + // contains more than one pubkey. If not, the first one is returned. If yes, they're + // checked for whether they yield at least one output + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0), error::wallet_internal_error, + "Public key wasn't found in the transaction extra"); + const crypto::public_key tx_pub_key = pub_key_field.pub_key; + bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); + if (!two_found) { + // easy case, just one found + return tx_pub_key; + } + + // more than one, loop and search + const cryptonote::account_keys& keys = m_account.get_keys(); + size_t pk_index = 0; + while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { + const crypto::public_key tx_pub_key = pub_key_field.pub_key; + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); + + for (size_t i = 0; i < td.m_tx.vout.size(); ++i) + { + uint64_t money_transfered = 0; + bool error = false, received = false; + check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, received, money_transfered, error); + if (!error && received) + return tx_pub_key; + } + } + + // we found no key yielding an output + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, + "Public key yielding at least one output wasn't found in the transaction extra"); + return cryptonote::null_pkey; +} +//---------------------------------------------------------------------------------------------------- std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key_images() const { std::vector<std::pair<crypto::key_image, crypto::signature>> ski; @@ -4713,10 +4788,8 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key } - tx_extra_pub_key pub_key_field; - THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, - "Public key wasn't found in the transaction extra"); - crypto::public_key tx_pub_key = pub_key_field.pub_key; + + crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); // generate ephemeral secret key crypto::key_image ki; @@ -4845,10 +4918,9 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i)); THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(td.m_tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format at index " + boost::lexical_cast<std::string>(i)); - THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, - "Public key wasn't found in the transaction extra at index " + boost::lexical_cast<std::string>(i)); + crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); - cryptonote::generate_key_image_helper(m_account.get_keys(), pub_key_field.pub_key, td.m_internal_output_index, in_ephemeral, td.m_key_image); + cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, td.m_internal_output_index, in_ephemeral, td.m_key_image); td.m_key_image_known = true; THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i)); @@ -4889,14 +4961,15 @@ std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, //---------------------------------------------------------------------------------------------------- std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const { - THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(chacha8_iv), - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); + const size_t prefix_size = sizeof(chacha8_iv) + (authenticated ? sizeof(crypto::signature) : 0); + THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size, + error::wallet_internal_error, "Unexpected ciphertext size"); crypto::chacha8_key key; crypto::generate_chacha8_key(&skey, sizeof(skey), key); const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0]; std::string plaintext; - plaintext.resize(ciphertext.size() - sizeof(iv) - (authenticated ? sizeof(crypto::signature) : 0)); + plaintext.resize(ciphertext.size() - prefix_size); if (authenticated) { crypto::hash hash; @@ -4907,7 +4980,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature), error::wallet_internal_error, "Failed to authenticate criphertext"); } - crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - sizeof(iv), key, iv, &plaintext[0]); + crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); return std::move(plaintext); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 6168873d5..b6d3250b2 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -191,11 +191,12 @@ namespace tools { std::vector<cryptonote::tx_source_entry> sources; cryptonote::tx_destination_entry change_dts; - std::vector<cryptonote::tx_destination_entry> splitted_dsts; + std::vector<cryptonote::tx_destination_entry> splitted_dsts; // split, includes change std::list<size_t> selected_transfers; std::vector<uint8_t> extra; uint64_t unlock_time; bool use_rct; + std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change BEGIN_SERIALIZE_OBJECT() FIELD(sources) @@ -205,12 +206,16 @@ namespace tools FIELD(extra) VARINT_FIELD(unlock_time) FIELD(use_rct) + FIELD(dests) END_SERIALIZE() }; typedef std::vector<transfer_details> transfer_container; typedef std::unordered_multimap<crypto::hash, payment_details> payment_container; + // The convention for destinations is: + // dests does not include change + // splitted_dsts (in construction_data) does struct pending_tx { cryptonote::transaction tx; @@ -591,6 +596,7 @@ namespace tools template<typename entry> void get_outs(std::vector<std::vector<entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); + crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; cryptonote::account_base m_account; std::string m_daemon_address; @@ -1047,6 +1053,7 @@ namespace tools ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; + ptx.construction_data.dests = dsts; } |