aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/node_rpc_proxy.cpp1
-rw-r--r--src/wallet/ringdb.cpp1
-rw-r--r--src/wallet/wallet2.cpp95
-rw-r--r--src/wallet/wallet2.h6
4 files changed, 79 insertions, 24 deletions
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 346c052b5..605531e59 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -28,7 +28,6 @@
#include "node_rpc_proxy.h"
#include "rpc/core_rpc_server_commands_defs.h"
-#include "common/json_util.h"
#include "storages/http_abstract_invoke.h"
using namespace epee;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index f562d6c06..b69022af4 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -30,6 +30,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/filesystem.hpp>
+#include "common/util.h"
#include "misc_log_ex.h"
#include "misc_language.h"
#include "wallet_errors.h"
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 42a5b3fda..202a8ef75 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
@@ -815,7 +830,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
+ }
}
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
@@ -2149,7 +2169,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)
@@ -2852,10 +2872,11 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
req.amounts.push_back(0);
req.from_height = 0;
- req.cumulative = true;
+ req.cumulative = false;
req.binary = true;
+ req.compress = true;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
+ bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
if (!r)
{
@@ -2882,6 +2903,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
MWARNING("Failed to request output distribution: results are not for amount 0");
return false;
}
+ for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i)
+ res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1];
start_height = res.distributions[0].data.start_height;
distribution = std::move(res.distributions[0].data.distribution);
return true;
@@ -3145,7 +3168,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account_data = buffer.GetString();
// Encrypt the entire JSON object.
- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string cipher;
cipher.resize(account_data.size());
keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
@@ -6043,7 +6065,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;
@@ -6077,7 +6099,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;
@@ -7186,6 +7208,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
// get the keys for those
+ req.get_txid = false;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -7517,30 +7540,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");
}
@@ -7608,8 +7657,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()});
@@ -7671,7 +7720,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())
@@ -7679,7 +7728,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;
@@ -7687,7 +7736,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;
}
@@ -7699,7 +7748,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");
@@ -11270,7 +11319,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");
@@ -11281,8 +11330,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())
@@ -11341,7 +11391,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());
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index eb0763131..0ba8e4c7d 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -37,6 +37,7 @@
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/deque.hpp>
+#include <boost/thread/lock_guard.hpp>
#include <atomic>
#include "include_base_utils.h"
@@ -49,6 +50,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "common/unordered_containers_boost_serialization.h"
+#include "common/util.h"
#include "crypto/chacha.h"
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
@@ -374,7 +376,7 @@ namespace tools
struct multisig_sig
{
rct::rctSig sigs;
- crypto::public_key ignore;
+ std::unordered_set<crypto::public_key> ignore;
std::unordered_set<rct::key> used_L;
std::unordered_set<crypto::public_key> signing_keys;
rct::multisig_out msout;
@@ -1256,7 +1258,7 @@ namespace tools
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
void trim_hashchain();
crypto::key_image get_multisig_composite_key_image(size_t n) const;
- rct::multisig_kLRki 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 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;
rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const;
rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const;
void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n);