aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp99
1 files changed, 72 insertions, 27 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 46bd47353..097278961 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -67,6 +67,7 @@ using namespace epee;
#include "common/json_util.h"
#include "memwipe.h"
#include "common/base58.h"
+#include "common/combinator.h"
#include "common/dns_utils.h"
#include "common/notify.h"
#include "common/perf_timer.h"
@@ -177,6 +178,20 @@ namespace
return public_keys;
}
+
+ bool keys_intersect(const std::unordered_set<crypto::public_key>& s1, const std::unordered_set<crypto::public_key>& s2)
+ {
+ if (s1.empty() || s2.empty())
+ return false;
+
+ for (const auto& e: s1)
+ {
+ if (s2.find(e) != s2.end())
+ return true;
+ }
+
+ return false;
+ }
}
namespace
@@ -818,7 +833,12 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
{
if (!locked)
return;
- w.encrypt_keys(key);
+ try { w.encrypt_keys(key); }
+ catch (...)
+ {
+ MERROR("Failed to re-encrypt wallet keys");
+ // do not propagate through dtor, we'd crash
+ }
}
void wallet_device_callback::on_button_request()
@@ -1386,14 +1406,12 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi
//----------------------------------------------------------------------------------------------------
void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
{
- const cryptonote::account_keys& keys = m_account.get_keys();
-
if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields))
{
// Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
- tx_cache_data.tx_extra_fields.clear();
- return;
+ if (tx_cache_data.tx_extra_fields.empty())
+ return;
}
// Don't try to extract tx public key if tx has no ouputs
@@ -2179,7 +2197,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
{
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
- tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
+ tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
}
++txidx;
for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
@@ -4336,7 +4354,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
return make_multisig(password, secret_keys, public_keys, threshold);
}
-bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers)
+bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
{
exchange_multisig_keys(password, pkeys, signers);
return true;
@@ -6087,7 +6105,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
for (auto &sig: ptx.multisig_sigs)
{
- if (sig.ignore != local_signer)
+ if (sig.ignore.find(local_signer) == sig.ignore.end())
{
ptx.tx.rct_signatures = sig.sigs;
@@ -6121,7 +6139,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
bool found = false;
for (const auto &sig: ptx.multisig_sigs)
{
- if (sig.ignore != local_signer && exported_txs.m_signers.find(sig.ignore) == exported_txs.m_signers.end())
+ if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.m_signers))
{
THROW_WALLET_EXCEPTION_IF(found, error::wallet_internal_error, "More than one transaction is final");
ptx.tx.rct_signatures = sig.sigs;
@@ -7562,30 +7580,56 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
// if this is a multisig wallet, create a list of multisig signers we can use
std::deque<crypto::public_key> multisig_signers;
size_t n_multisig_txes = 0;
+ std::vector<std::unordered_set<crypto::public_key>> ignore_sets;
if (m_multisig && !m_transfers.empty())
{
const crypto::public_key local_signer = get_multisig_signer_public_key();
size_t n_available_signers = 1;
+
+ // At this step we need to define set of participants available for signature,
+ // i.e. those of them who exchanged with multisig info's
for (const crypto::public_key &signer: m_multisig_signers)
{
if (signer == local_signer)
continue;
- multisig_signers.push_front(signer);
for (const auto &i: m_transfers[0].m_multisig_info)
{
if (i.m_signer == signer)
{
- multisig_signers.pop_front();
multisig_signers.push_back(signer);
++n_available_signers;
break;
}
}
}
- multisig_signers.push_back(local_signer);
+ // n_available_signers includes the transaction creator, but multisig_signers doesn't
MDEBUG("We can use " << n_available_signers << "/" << m_multisig_signers.size() << " other signers");
- THROW_WALLET_EXCEPTION_IF(n_available_signers+1 < m_multisig_threshold, error::multisig_import_needed);
- n_multisig_txes = n_available_signers == m_multisig_signers.size() ? m_multisig_threshold : 1;
+ THROW_WALLET_EXCEPTION_IF(n_available_signers < m_multisig_threshold, error::multisig_import_needed);
+ if (n_available_signers > m_multisig_threshold)
+ {
+ // If there more potential signers (those who exchanged with multisig info)
+ // than threshold needed some of them should be skipped since we don't know
+ // who will sign tx and who won't. Hence we don't contribute their LR pairs to the signature.
+
+ // We create as many transactions as many combinations of excluded signers may be.
+ // For example, if we have 2/4 wallet and wallets are: A, B, C and D. Let A be
+ // transaction creator, so we need just 1 signature from set of B, C, D.
+ // Using "excluding" logic here we have to exclude 2-of-3 wallets. Combinations go as follows:
+ // BC, BD, and CD. We save these sets to use later and counting the number of required txs.
+ tools::Combinator<crypto::public_key> c(std::vector<crypto::public_key>(multisig_signers.begin(), multisig_signers.end()));
+ auto ignore_combinations = c.combine(multisig_signers.size() + 1 - m_multisig_threshold);
+ for (const auto& combination: ignore_combinations)
+ {
+ ignore_sets.push_back(std::unordered_set<crypto::public_key>(combination.begin(), combination.end()));
+ }
+
+ n_multisig_txes = ignore_sets.size();
+ }
+ else
+ {
+ // If we have exact count of signers just to fit in threshold we don't exclude anyone and create 1 transaction
+ n_multisig_txes = 1;
+ }
MDEBUG("We will create " << n_multisig_txes << " txes");
}
@@ -7653,8 +7697,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
src.mask = td.m_mask;
if (m_multisig)
{
- crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front();
- src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore, used_L, used_L);
+ auto ignore_set = ignore_sets.empty() ? std::unordered_set<crypto::public_key>() : ignore_sets.front();
+ src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore_set, used_L, used_L);
}
else
src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()});
@@ -7716,7 +7760,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
std::vector<tools::wallet2::multisig_sig> multisig_sigs;
if (m_multisig)
{
- crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front();
+ auto ignore = ignore_sets.empty() ? std::unordered_set<crypto::public_key>() : ignore_sets.front();
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())
@@ -7724,7 +7768,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
const crypto::hash prefix_hash = cryptonote::get_transaction_prefix_hash(tx);
// create the other versions, one for every other participant (the first one's already done above)
- for (size_t signer_index = 1; signer_index < n_multisig_txes; ++signer_index)
+ for (size_t ignore_index = 1; ignore_index < ignore_sets.size(); ++ignore_index)
{
std::unordered_set<rct::key> new_used_L;
size_t src_idx = 0;
@@ -7732,7 +7776,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
for(size_t idx: selected_transfers)
{
cryptonote::tx_source_entry& src = sources_copy[src_idx];
- src.multisig_kLRki = get_multisig_composite_kLRki(idx, multisig_signers[signer_index], used_L, new_used_L);
+ src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore_sets[ignore_index], used_L, new_used_L);
++src_idx;
}
@@ -7744,7 +7788,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_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_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, std::unordered_set<crypto::public_key>(), msout});
+ multisig_sigs.push_back({ms_tx.rct_signatures, ignore_sets[ignore_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");
@@ -8150,7 +8194,6 @@ void wallet2::light_wallet_get_address_txs()
// for balance calculation
uint64_t wallet_total_sent = 0;
- uint64_t wallet_total_unlocked_sent = 0;
// txs in pool
std::vector<crypto::hash> pool_txs;
@@ -10467,7 +10510,7 @@ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& w
return m_account_tags;
}
-void wallet2::set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag)
+void wallet2::set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag)
{
for (uint32_t account_index : account_indices)
{
@@ -11317,7 +11360,7 @@ rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) con
return kLRki;
}
//----------------------------------------------------------------------------------------------------
-rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const
+rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const
{
CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index");
@@ -11328,8 +11371,9 @@ rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto
size_t n_signers_used = 1;
for (const auto &p: m_transfers[n].m_multisig_info)
{
- if (p.m_signer == ignore)
+ if (ignore_set.find(p.m_signer) != ignore_set.end())
continue;
+
for (const auto &lr: p.m_LR)
{
if (used_L.find(lr.m_L) != used_L.end())
@@ -11374,7 +11418,6 @@ cryptonote::blobdata wallet2::export_multisig()
for (size_t n = 0; n < m_transfers.size(); ++n)
{
transfer_details &td = m_transfers[n];
- const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
crypto::key_image ki;
td.m_multisig_k.clear();
info[n].m_LR.clear();
@@ -11388,7 +11431,10 @@ cryptonote::blobdata wallet2::export_multisig()
info[n].m_partial_key_images.push_back(ki);
}
- size_t nlr = m_multisig_threshold < m_multisig_signers.size() ? m_multisig_threshold - 1 : 1;
+ // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows:
+ // if we have 2/4 wallet with signers: A, B, C, D and A is a transaction creator it will need to pick up 1 signer from 3 wallets left.
+ // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1).
+ size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1);
for (size_t m = 0; m < nlr; ++m)
{
td.m_multisig_k.push_back(rct::skGen());
@@ -11403,7 +11449,6 @@ cryptonote::blobdata wallet2::export_multisig()
boost::archive::portable_binary_oarchive ar(oss);
ar << info;
- std::string magic(MULTISIG_EXPORT_FILE_MAGIC, strlen(MULTISIG_EXPORT_FILE_MAGIC));
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
std::string header;
header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));