aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/transaction_history.cpp3
-rw-r--r--src/wallet/api/utils.cpp2
-rw-r--r--src/wallet/api/wallet.cpp22
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/wallet2.cpp206
-rw-r--r--src/wallet/wallet2.h13
-rw-r--r--src/wallet/wallet2_api.h1
7 files changed, 177 insertions, 71 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/utils.cpp b/src/wallet/api/utils.cpp
index 1bf35197c..7de9d1927 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -61,7 +61,7 @@ bool isAddressLocal(const std::string &address)
// resolve to IP
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
- boost::asio::ip::tcp::resolver::query query(u_c.host, "");
+ boost::asio::ip::tcp::resolver::query query(u_c.host, "", boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query);
while (i != boost::asio::ip::tcp::resolver::iterator())
{
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index d21d8b900..97aaf2785 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -399,6 +399,11 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id) const
return m_wallet->get_account().get_public_integrated_address_str(pid, m_wallet->testnet());
}
+std::string WalletImpl::path() const
+{
+ return m_wallet->path();
+}
+
bool WalletImpl::store(const std::string &path)
{
clearStatus();
@@ -605,6 +610,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;
@@ -895,11 +911,11 @@ bool WalletImpl::connectToDaemon()
Wallet::ConnectionStatus WalletImpl::connected() const
{
- bool same_version = false;
- bool is_connected = m_wallet->check_connection(&same_version);
+ uint32_t version = 0;
+ bool is_connected = m_wallet->check_connection(&version);
if (!is_connected)
return Wallet::ConnectionStatus_Disconnected;
- if (!same_version)
+ if ((version >> 16) != CORE_RPC_VERSION_MAJOR)
return Wallet::ConnectionStatus_WrongVersion;
return Wallet::ConnectionStatus_Connected;
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 02a46da8c..5e4a64ff8 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -64,6 +64,7 @@ public:
bool setPassword(const std::string &password);
std::string address() const;
std::string integratedAddress(const std::string &payment_id) const;
+ std::string path() const;
bool store(const std::string &path);
std::string filename() const;
std::string keysFilename() const;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 15a134257..a15233a87 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;
+ }
}
}
}
@@ -2195,7 +2221,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::check_connection(bool *same_version)
+bool wallet2::check_connection(uint32_t *version)
{
boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
@@ -2213,7 +2239,7 @@ bool wallet2::check_connection(bool *same_version)
return false;
}
- if (same_version)
+ if (version)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
@@ -2222,9 +2248,9 @@ bool wallet2::check_connection(bool *same_version)
req_t.method = "get_version";
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
if (!r || resp_t.result.status != CORE_RPC_STATUS_OK)
- *same_version = false;
+ *version = 0;
else
- *same_version = resp_t.result.version == CORE_RPC_VERSION;
+ *version = resp_t.result.version;
}
return true;
@@ -2327,6 +2353,11 @@ void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain[0], error::wallet_internal_error, what);
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::path() const
+{
+ return m_wallet_file;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::store()
{
store_to("", "");
@@ -2730,7 +2761,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 +3088,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 +3366,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 +3473,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 +3687,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 +3808,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 +4721,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 +4793,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 +4923,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 +4966,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 +4985,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..f62a0f15b 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;
@@ -327,6 +332,8 @@ namespace tools
*/
void store_to(const std::string &path, const std::string &password);
+ std::string path() const;
+
/*!
* \brief verifies given password is correct for default wallet keys file
*/
@@ -403,7 +410,7 @@ namespace tools
std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
- bool check_connection(bool *same_version = NULL);
+ bool check_connection(uint32_t *version = NULL);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
@@ -591,6 +598,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 +1055,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;
}
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 185d1c009..60907b436 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -195,6 +195,7 @@ struct Wallet
virtual std::string errorString() const = 0;
virtual bool setPassword(const std::string &password) = 0;
virtual std::string address() const = 0;
+ virtual std::string path() const = 0;
/*!
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.