aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/utils.cpp5
-rw-r--r--src/wallet/api/wallet2_api.h1
-rw-r--r--src/wallet/wallet2.cpp256
-rw-r--r--src/wallet/wallet2.h10
-rw-r--r--src/wallet/wallet_errors.h11
-rw-r--r--src/wallet/wallet_rpc_server.cpp413
-rw-r--r--src/wallet/wallet_rpc_server.h5
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h14
8 files changed, 381 insertions, 334 deletions
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index a9646e038..e54dd9f1c 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -48,6 +48,11 @@ bool isAddressLocal(const std::string &address)
}
}
+void onStartup()
+{
+ tools::on_startup();
+}
+
}
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 8593bd1f9..ab1a48d6e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -42,6 +42,7 @@ namespace Monero {
namespace Utils {
bool isAddressLocal(const std::string &hostaddr);
+ void onStartup();
}
template<typename T>
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 90977b20a..2af41f588 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -53,6 +53,7 @@ using namespace epee;
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
+#include "serialization/string.h"
#include "cryptonote_basic/blobdatatype.h"
#include "mnemonics/electrum-words.h"
#include "common/i18n.h"
@@ -62,7 +63,7 @@ using namespace epee;
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "common/base58.h"
#include "ringct/rctSigs.h"
@@ -144,6 +145,16 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
}
+std::string get_size_string(size_t sz)
+{
+ return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)";
+}
+
+std::string get_size_string(const cryptonote::blobdata &tx)
+{
+ return get_size_string(tx.size());
+}
+
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
@@ -600,6 +611,7 @@ wallet2::wallet2(bool testnet, bool restricted):
m_min_output_value(0),
m_merge_destinations(false),
m_confirm_backlog(true),
+ m_confirm_backlog_threshold(0),
m_is_initialized(false),
m_restricted(restricted),
is_old_file_format(false),
@@ -723,6 +735,70 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
return true;
}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const
+{
+ bool ready;
+ uint32_t threshold, total;
+ if (!multisig(&ready, &threshold, &total))
+ {
+ std::cout << "This is not a multisig wallet" << std::endl;
+ return false;
+ }
+ if (!ready)
+ {
+ std::cout << "This multisig wallet is not yet finalized" << std::endl;
+ return false;
+ }
+ if (!raw && seed_language.empty())
+ {
+ std::cout << "seed_language not set" << std::endl;
+ return false;
+ }
+
+ crypto::secret_key skey;
+ crypto::public_key pkey;
+ const account_keys &keys = get_account().get_keys();
+ std::string data;
+ data.append((const char*)&threshold, sizeof(uint32_t));
+ data.append((const char*)&total, sizeof(uint32_t));
+ skey = keys.m_spend_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_spend_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ skey = keys.m_view_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_view_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ for (const auto &skey: keys.m_multisig_keys)
+ data.append((const char*)&skey, sizeof(skey));
+ for (const auto &signer: m_multisig_signers)
+ data.append((const char*)&signer, sizeof(signer));
+
+ if (!passphrase.empty())
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ data = encrypt(data, key, true);
+ }
+
+ if (raw)
+ {
+ seed = epee::string_tools::buff_to_hex_nodelimer(data);
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
+ {
+ std::cout << "Failed to encode seed";
+ return false;
+ }
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -1960,6 +2036,11 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
pull_hashes(0, blocks_start_height, short_chain_history, hashes);
if (hashes.size() <= 3)
return;
+ if (blocks_start_height < m_blockchain.offset())
+ {
+ MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
+ return;
+ }
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
std::list<crypto::hash>::iterator right = hashes.end();
@@ -2636,6 +2717,97 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
+ * \param multisig_data The multisig restore info and keys
+ */
+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data)
+{
+ clear();
+ prepare_file_names(wallet_);
+
+ if (!wallet_.empty())
+ {
+ boost::system::error_code ignored_ec;
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
+ }
+
+ m_account.generate(rct::rct2sk(rct::zero()), true, false);
+
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() < 32, error::invalid_multisig_seed);
+ size_t offset = 0;
+ uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ THROW_WALLET_EXCEPTION_IF(threshold < 2, error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(total != threshold && total != threshold + 1, error::invalid_multisig_seed);
+ const size_t n_multisig_keys = total == threshold ? 1 : threshold;
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
+
+ std::vector<crypto::secret_key> multisig_keys;
+ std::vector<crypto::public_key> multisig_signers;
+ crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ for (size_t n = 0; n < n_multisig_keys; ++n)
+ {
+ multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::secret_key);
+ }
+ for (size_t n = 0; n < total; ++n)
+ {
+ multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::public_key);
+ }
+
+ crypto::public_key calculated_view_public_key;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
+ crypto::public_key local_signer;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(spend_secret_key, local_signer), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
+ rct::key skey = rct::zero();
+ for (const auto &msk: multisig_keys)
+ sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
+ THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
+
+ m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
+ m_account.finalize_multisig(spend_public_key);
+
+ m_account_public_address = m_account.get_keys().m_account_address;
+ m_watch_only = false;
+ m_multisig = true;
+ m_multisig_threshold = threshold;
+ m_multisig_signers = multisig_signers;
+
+ if (!wallet_.empty())
+ {
+ bool r = store_keys(m_keys_file, password, false);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet));
+ if(!r) MERROR("String with address text not saved");
+ }
+
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ add_subaddress_account(tr("Primary account"));
+
+ if (!wallet_.empty())
+ store();
+}
+
+/*!
+ * \brief Generates a wallet or restores one.
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
* \param recovery_param If it is a restore, the recovery key
* \param recover Whether it is a restore
* \param two_random Whether it is a non-deterministic wallet
@@ -5667,7 +5839,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
if (m_multisig)
{
crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front();
- multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, {}, msout});
+ multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, std::unordered_set<crypto::public_key>(), msout});
if (m_multisig_threshold < m_multisig_signers.size())
{
@@ -5694,7 +5866,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
- multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, {}, msout});
+ multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
ms_tx.rct_signatures = tx.rct_signatures;
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_hash(ms_tx) != cryptonote::get_transaction_hash(tx), error::wallet_internal_error, "Multisig txes differ by more than the signatures");
@@ -5993,7 +6165,8 @@ void wallet2::light_wallet_get_unspent_outs()
add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
td.m_key_image = unspent_key_image;
- td.m_key_image_known = !m_watch_only;
+ td.m_key_image_known = !m_watch_only && !m_multisig;
+ td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
@@ -6667,6 +6840,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ uint64_t inputs = 0, outputs = needed_fee;
+ for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
+ for (const auto &o: tx.dsts) outputs += o.amount;
+
+ if (inputs < outputs)
+ {
+ LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
+ adding_fee = true;
+ goto skip_tx;
+ }
+
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
tx.selected_transfers.size() << " inputs");
if (use_rct)
@@ -6678,7 +6862,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
@@ -6720,11 +6904,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6742,6 +6926,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
+skip_tx:
// if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
// pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
@@ -6776,7 +6961,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -6794,37 +6979,48 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
- std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
-
- if (subaddr_indices.empty())
- {
- // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
- if (balance_per_subaddr.count(0) == 1 && balance_per_subaddr.size() > 1)
- balance_per_subaddr.erase(0);
- auto i = balance_per_subaddr.begin();
- std::advance(i, crypto::rand<size_t>() % balance_per_subaddr.size());
- subaddr_indices.insert(i->first);
- }
- for (uint32_t i : subaddr_indices)
- LOG_PRINT_L2("Spending from subaddress index " << i);
+ std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
- // gather all dust and non-dust outputs of specified subaddress
+ // gather all dust and non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
+ bool fund_found = false;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
+ fund_found = true;
if (below == 0 || td.amount() < below)
{
if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
else
- unused_dust_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
}
}
}
+ THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
+ THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
- THROW_WALLET_EXCEPTION_IF(unused_transfers_indices.empty() && unused_dust_indices.empty(), error::not_enough_money, 0, 0, 0); // not sure if a new error class (something like 'cant_sweep_empty'?) should be introduced
+ if (subaddr_indices.empty())
+ {
+ // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
+ if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
+ unused_transfer_dust_indices_per_subaddr.erase(0);
+ auto i = unused_transfer_dust_indices_per_subaddr.begin();
+ std::advance(i, crypto::rand<size_t>() % unused_transfer_dust_indices_per_subaddr.size());
+ unused_transfers_indices = i->second.first;
+ unused_dust_indices = i->second.second;
+ LOG_PRINT_L2("Spending from subaddress index " << i->first);
+ }
+ else
+ {
+ for (const auto& p : unused_transfer_dust_indices_per_subaddr)
+ {
+ unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
+ unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
+ LOG_PRINT_L2("Spending from subaddress index " << p.first);
+ }
+ }
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
@@ -6928,7 +7124,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@@ -6944,11 +7140,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6975,7 +7171,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index b1115f67b..2fbe05f89 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -436,6 +436,15 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
+ * \brief Generates a wallet or restores one.
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param multisig_data The multisig restore info and keys
+ */
+ void generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data);
+
+ /*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
@@ -610,6 +619,7 @@ namespace tools
bool watch_only() const { return m_watch_only; }
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
bool has_multisig_partial_key_images() const;
+ bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 234c22d85..023b53f28 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -60,6 +60,7 @@ namespace tools
// file_save_error
// invalid_password
// invalid_priority
+ // invalid_multisig_seed
// refresh_error *
// acc_outs_lookup_error
// block_parse_error
@@ -266,6 +267,16 @@ namespace tools
std::string to_string() const { return wallet_logic_error::to_string(); }
};
+ struct invalid_multisig_seed : public wallet_logic_error
+ {
+ explicit invalid_multisig_seed(std::string&& loc)
+ : wallet_logic_error(std::move(loc), "invalid multisig seed")
+ {
+ }
+
+ std::string to_string() const { return wallet_logic_error::to_string(); }
+ };
+
//----------------------------------------------------------------------------------------------------
struct invalid_pregenerated_random : public wallet_logic_error
{
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index f031b765d..e53fd6fce 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -89,6 +89,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
+ if (m_wallet)
+ delete m_wallet;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -229,8 +231,9 @@ namespace tools
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
m_net_server.set_threads_prefix("RPC");
+ auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -255,6 +258,7 @@ namespace tools
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
@@ -280,6 +284,7 @@ namespace tools
entry.type = "out";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
@@ -298,6 +303,7 @@ namespace tools
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
@@ -316,6 +322,7 @@ namespace tools
entry.double_spend_seen = ppd.m_double_spend_seen;
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
@@ -662,7 +669,98 @@ namespace tools
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ static std::string ptx_to_string(const tools::wallet2::pending_tx &ptx)
+ {
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << ptx;
+ }
+ catch (...)
+ {
+ return "";
+ }
+ return epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T> static bool is_error_value(const T &val) { return false; }
+ static bool is_error_value(const std::string &s) { return s.empty(); }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(T &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where = std::move(s);
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(std::list<T> &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where.emplace_back(std::move(s));
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ static uint64_t total_amount(const tools::wallet2::pending_tx &ptx)
+ {
+ uint64_t amount = 0;
+ for (const auto &dest: ptx.dests) amount += dest.amount;
+ return amount;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename Ts, typename Tu>
+ bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er)
+ {
+ for (const auto & ptx : ptx_vector)
+ {
+ if (get_tx_key)
+ {
+ std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
+ s += epee::string_tools::pod_to_hex(additional_tx_key);
+ fill(tx_key, s);
+ }
+ // Compute amount leaving wallet in tx. By convention dests does not include change outputs
+ fill(amount, total_amount(ptx));
+ fill(fee, ptx.fee);
+ }
+ if (m_wallet->multisig())
+ {
+ multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
+ if (multisig_txset.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save multisig tx set after creation";
+ return false;
+ }
+ }
+ else
+ {
+ if (!do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
+
+ // populate response with tx hashes
+ for (auto & ptx : ptx_vector)
+ {
+ bool r = fill(tx_hash, epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ r = r && (!get_tx_hex || fill(tx_blob, epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx))));
+ r = r && (!get_tx_metadata || fill(tx_metadata, ptx_to_string(ptx)));
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save tx info";
+ return false;
+ }
+ }
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er)
{
@@ -705,55 +803,8 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
- for (const crypto::secret_key& additional_tx_key : ptx_vector.back().additional_tx_keys)
- res.tx_key += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- res.fee = ptx_vector.back().fee;
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hash
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx_vector.back().tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx_vector.back();
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
{
@@ -786,82 +837,12 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- uint64_t ptx_amount;
- std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
- ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- // Compute amount leaving wallet in tx. By convention dests does not include change outputs
- ptx_amount = 0;
- for(auto & dt: ptx.dests)
- ptx_amount += dt.amount;
- res.amount_list.push_back(ptx_amount);
-
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
-
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (!req.do_not_relay)
- {
- LOG_PRINT_L2("on_transfer_split calling commit_tx");
- m_wallet->commit_tx(ptx_vector);
- LOG_PRINT_L2("on_transfer_split called commit_tx");
- }
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
-
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -885,69 +866,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -985,68 +905,8 @@ namespace tools
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -1112,63 +972,12 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
- }
- catch (const tools::error::daemon_busy& e)
- {
- er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
- er.message = e.what();
- return false;
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
{
- er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
- er.message = e.what();
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
return false;
}
catch (...)
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index b20198b78..88f2a85a4 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -207,6 +207,11 @@ namespace tools
bool not_open(epee::json_rpc::error& er);
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
+ template<typename Ts, typename Tu>
+ bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er);
+
wallet2 *m_wallet;
std::string m_wallet_dir;
tools::private_file rpc_login_file;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 76c02039b..4abbb8386 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -411,6 +411,7 @@ namespace wallet_rpc
std::string tx_hash;
std::string tx_key;
std::list<std::string> amount_keys;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -420,6 +421,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount_keys)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
@@ -520,14 +522,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -582,14 +586,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -631,6 +637,7 @@ namespace wallet_rpc
{
std::string tx_hash;
std::string tx_key;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -639,6 +646,7 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
@@ -1106,6 +1114,7 @@ namespace wallet_rpc
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ std::string address;
bool double_spend_seen;
BEGIN_KV_SERIALIZE_MAP()
@@ -1120,6 +1129,7 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(address);
KV_SERIALIZE(double_spend_seen)
END_KV_SERIALIZE_MAP()
};