aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/wallet.cpp27
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/api/wallet2_api.h8
-rw-r--r--src/wallet/api/wallet_manager.cpp5
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/node_rpc_proxy.cpp7
-rw-r--r--src/wallet/wallet2.cpp776
-rw-r--r--src/wallet/wallet2.h55
-rw-r--r--src/wallet/wallet_rpc_helpers.h1
-rw-r--r--src/wallet/wallet_rpc_server.cpp60
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h15
11 files changed, 296 insertions, 661 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 0afbda705..87242b79c 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -450,7 +450,7 @@ WalletImpl::~WalletImpl()
LOG_PRINT_L1(__FUNCTION__);
m_wallet->callback(NULL);
// Pause refresh thread - prevents refresh from starting again
- pauseRefresh();
+ WalletImpl::pauseRefresh(); // Call the method directly (not polymorphically) to protect against UB in destructor.
// Close wallet - stores cache and stops ongoing refresh operation
close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
@@ -1332,7 +1332,7 @@ MultisigState WalletImpl::multisig() const {
string WalletImpl::getMultisigInfo() const {
try {
clearStatus();
- return m_wallet->get_multisig_info();
+ return m_wallet->get_multisig_first_kex_msg();
} catch (const exception& e) {
LOG_ERROR("Error on generating multisig info: " << e.what());
setStatusError(string(tr("Failed to get multisig info: ")) + e.what());
@@ -1341,7 +1341,7 @@ string WalletImpl::getMultisigInfo() const {
return string();
}
-string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold) {
+string WalletImpl::makeMultisig(const vector<string>& info, const uint32_t threshold) {
try {
clearStatus();
@@ -1366,30 +1366,12 @@ std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &inf
return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info);
} catch (const exception& e) {
LOG_ERROR("Error on exchanging multisig keys: " << e.what());
- setStatusError(string(tr("Failed to make multisig: ")) + e.what());
+ setStatusError(string(tr("Failed to exchange multisig keys: ")) + e.what());
}
return string();
}
-bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) {
- try {
- clearStatus();
- checkMultisigWalletNotReady(m_wallet);
-
- if (m_wallet->finalize_multisig(epee::wipeable_string(m_password), extraMultisigInfo)) {
- return true;
- }
-
- setStatusError(tr("Failed to finalize multisig wallet creation"));
- } catch (const exception& e) {
- LOG_ERROR("Error on finalizing multisig wallet creation: " << e.what());
- setStatusError(string(tr("Failed to finalize multisig wallet creation: ")) + e.what());
- }
-
- return false;
-}
-
bool WalletImpl::exportMultisigImages(string& images) {
try {
clearStatus();
@@ -1760,6 +1742,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
extra_size,
m_wallet->use_fork_rules(8, 0),
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
+ m_wallet->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, 0),
m_wallet->get_base_fee(),
m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
m_wallet->get_fee_quantization_mask());
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 67fc2c08a..7e1e62081 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -147,7 +147,6 @@ public:
std::string getMultisigInfo() const override;
std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
std::string exchangeMultisigKeys(const std::vector<std::string> &info) override;
- bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
bool hasMultisigPartialKeyImages() const override;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index f9c421a93..6da547054 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -790,7 +790,7 @@ struct Wallet
/**
* @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets
* @param info - vector of multisig infos from other participants obtained with getMulitisInfo call
- * @param threshold - number of required signers to make valid transaction. Must be equal to number of participants (N) or N - 1
+ * @param threshold - number of required signers to make valid transaction. Must be <= number of participants
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
*/
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
@@ -801,12 +801,6 @@ struct Wallet
*/
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info) = 0;
/**
- * @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
- * @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
- * @return true if success
- */
- virtual bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) = 0;
- /**
* @brief exportMultisigImages - exports transfers' key images
* @param images - output paramter for hex encoded array of images
* @return true if success
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 417a27db5..f5d5e2168 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -49,6 +49,11 @@ namespace epee {
namespace Monero {
+WalletManagerImpl::WalletManagerImpl()
+{
+ tools::set_strict_default_file_permissions(true);
+}
+
Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password,
const std::string &language, NetworkType nettype, uint64_t kdf_rounds)
{
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index cf3056a17..1e8cff877 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -95,7 +95,7 @@ public:
bool setProxy(const std::string &address) override;
private:
- WalletManagerImpl() {}
+ WalletManagerImpl();
friend struct WalletManagerFactory;
net::http::client m_http_client;
std::string m_errorString;
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index a576c267c..148f957eb 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -306,7 +306,12 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_payment_info(bool mining, boo
m_rpc_payment_seed_height = resp_t.seed_height;
m_rpc_payment_cookie = resp_t.cookie;
- if (!epee::string_tools::parse_hexstr_to_binbuff(resp_t.hashing_blob, m_rpc_payment_blob) || m_rpc_payment_blob.size() < 43)
+ if (m_rpc_payment_diff == 0)
+ {
+ // If no payment required daemon doesn't give us back a hashing blob
+ m_rpc_payment_blob.clear();
+ }
+ else if (!epee::string_tools::parse_hexstr_to_binbuff(resp_t.hashing_blob, m_rpc_payment_blob) || m_rpc_payment_blob.size() < 43)
{
MERROR("Invalid hashing blob: " << resp_t.hashing_blob);
return std::string("Invalid hashing blob");
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 5a4cafc32..f2795b50f 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <algorithm>
#include <numeric>
#include <tuple>
#include <queue>
@@ -59,6 +60,8 @@ using namespace epee;
#include "misc_language.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "multisig/multisig.h"
+#include "multisig/multisig_account.h"
+#include "multisig/multisig_kex_msg.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
#include "common/threadpool.h"
@@ -146,10 +149,9 @@ using namespace cryptonote;
#define IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION 12
#define DEFAULT_UNLOCK_TIME (CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE * DIFFICULTY_TARGET_V2)
-#define RECENT_SPEND_WINDOW (50 * DIFFICULTY_TARGET_V2)
+#define RECENT_SPEND_WINDOW (15 * DIFFICULTY_TARGET_V2)
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
-static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1";
@@ -167,42 +169,6 @@ namespace
return dir.string();
}
- std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
- {
- std::string data;
- crypto::public_key signer;
- CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
- data += std::string((const char *)&signer, sizeof(crypto::public_key));
-
- for (const auto &key: keys)
- {
- data += std::string((const char *)&key, sizeof(crypto::public_key));
- }
-
- data.resize(data.size() + sizeof(crypto::signature));
-
- crypto::hash hash;
- crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
- crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
- crypto::generate_signature(hash, signer, signer_secret_key, signature);
-
- return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
- }
-
- std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
- {
- std::vector<crypto::public_key> public_keys;
- public_keys.reserve(keys.size());
-
- std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
- crypto::public_key p;
- CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
- return p;
- });
-
- 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())
@@ -314,7 +280,6 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
{
keys_file = file_path;
wallet_file = file_path;
- boost::system::error_code e;
if(string_tools::get_extension(keys_file) == "keys")
{//provided keys file name
wallet_file = string_tools::cut_off_extension(wallet_file);
@@ -816,7 +781,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
}
}
-size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
size_t size = 0;
@@ -840,12 +805,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
size += 1;
// rangeSigs
- if (bulletproof)
+ if (bulletproof || bulletproof_plus)
{
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
- size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
+ size += (2 * (6 + log_padded_outputs) + (bulletproof_plus ? 6 : (4 + 5))) * 32 + 3;
}
else
size += (2*64*32+32+64*32) * n_outputs;
@@ -868,29 +833,29 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee
size += 4;
- LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ LOG_PRINT_L2("estimated " << (bulletproof_plus ? "bulletproof plus" : bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
-size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
-uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
- size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
- if (use_rct && bulletproof && n_outputs > 2)
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
+ if (use_rct && (bulletproof || bulletproof_plus) && n_outputs > 2)
{
- const uint64_t bp_base = 368;
+ const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
- const uint64_t bp_size = 32 * (9 + nlr);
+ const uint64_t bp_size = 32 * ((bulletproof_plus ? 6 : 9) + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
@@ -903,6 +868,11 @@ uint8_t get_bulletproof_fork()
return 8;
}
+uint8_t get_bulletproof_plus_fork()
+{
+ return HF_VERSION_BULLETPROOF_PLUS;
+}
+
uint8_t get_clsag_fork()
{
return HF_VERSION_CLSAG;
@@ -1024,13 +994,7 @@ gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shap
end = rct_offsets.data() + rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
num_rct_outputs = *(end - 1);
THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs");
- THROW_WALLET_EXCEPTION_IF(outputs_to_consider == 0, error::wallet_internal_error, "No rct outputs to consider");
- average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range
- if (average_output_time == 0) {
- // TODO: apply this to all cases; do so alongside a hard fork, where all clients will update at the same time, preventing anonymity puddle formation
- average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / static_cast<double>(outputs_to_consider);
- }
- THROW_WALLET_EXCEPTION_IF(average_output_time == 0, error::wallet_internal_error, "Average seconds per output cannot be 0.");
+ average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / static_cast<double>(outputs_to_consider); // this assumes constant target over the whole rct range
};
gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {}
@@ -1209,6 +1173,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_ignore_outputs_above(MONEY_SUPPLY),
m_ignore_outputs_below(0),
m_track_uses(false),
+ m_show_wallet_name_when_locked(false),
m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
m_setup_background_mining(BackgroundMiningMaybe),
m_persistent_rpc_client_id(false),
@@ -1235,8 +1200,6 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_ring_history_saved(false),
m_ringdb(),
m_last_block_reward(0),
- m_encrypt_keys_after_refresh(boost::none),
- m_decrypt_keys_lockers(0),
m_unattended(unattended),
m_devices_registered(false),
m_device_last_key_image_sync(0),
@@ -1859,6 +1822,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
+ case rct::RCTTypeBulletproofPlus:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
@@ -1888,8 +1852,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
- decrypt_keys(*pwd);
- m_encrypt_keys_after_refresh = *pwd;
+ m_encrypt_keys_after_refresh.reset(new wallet_keys_unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, *pwd));
}
}
@@ -3021,11 +2984,7 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
MTRACE("update_pool_state start");
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
- if (m_encrypt_keys_after_refresh)
- {
- encrypt_keys(*m_encrypt_keys_after_refresh);
- m_encrypt_keys_after_refresh = boost::none;
- }
+ m_encrypt_keys_after_refresh.reset();
});
// get the pool state
@@ -3456,11 +3415,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
start_height = 0;
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
- if (m_encrypt_keys_after_refresh)
- {
- encrypt_keys(*m_encrypt_keys_after_refresh);
- m_encrypt_keys_after_refresh = boost::none;
- }
+ m_encrypt_keys_after_refresh.reset();
});
auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);});
@@ -4018,6 +3973,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
value2.SetInt(m_track_uses ? 1 : 0);
json.AddMember("track_uses", value2, json.GetAllocator());
+ value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0);
+ json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator());
+
value2.SetInt(m_inactivity_lock_timeout);
json.AddMember("inactivity_lock_timeout", value2, json.GetAllocator());
@@ -4202,6 +4160,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_ignore_outputs_above = MONEY_SUPPLY;
m_ignore_outputs_below = 0;
m_track_uses = false;
+ m_show_wallet_name_when_locked = false;
m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT;
m_setup_background_mining = BackgroundMiningMaybe;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
@@ -4376,6 +4335,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_ignore_outputs_below = field_ignore_outputs_below;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
m_track_uses = field_track_uses;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, show_wallet_name_when_locked, int, Int, false, false);
+ m_show_wallet_name_when_locked = field_show_wallet_name_when_locked;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, inactivity_lock_timeout, uint32_t, Uint, false, DEFAULT_INACTIVITY_LOCK_TIMEOUT);
m_inactivity_lock_timeout = field_inactivity_lock_timeout;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
@@ -4467,7 +4428,26 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_account.set_device(hwdev);
account_public_address device_account_public_address;
- THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
+ bool fetch_device_address = true;
+
+ ::hw::device_cold* dev_cold = nullptr;
+ if (m_key_device_type == hw::device::device_type::TREZOR && (dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev)) != nullptr) {
+ THROW_WALLET_EXCEPTION_IF(!dev_cold->get_public_address_with_no_passphrase(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
+ if (device_account_public_address == m_account.get_keys().m_account_address) {
+ LOG_PRINT_L0("Wallet opened with an empty passphrase");
+ fetch_device_address = false;
+ dev_cold->set_use_empty_passphrase(true);
+ } else {
+ fetch_device_address = true;
+ LOG_PRINT_L0("Wallet opening with an empty passphrase failed. Retry again: " << fetch_device_address);
+ dev_cold->reset_session();
+ }
+ }
+
+ if (fetch_device_address) {
+ THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
+ }
+
THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. If the device uses the passphrase feature, please check whether the passphrase was entered correctly (it may have been misspelled - different passphrases generate different wallets, passphrase is case-sensitive). "
"Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
", wallet address: " + m_account.get_public_address_str(m_nettype));
@@ -4581,18 +4561,12 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
void wallet2::encrypt_keys(const crypto::chacha_key &key)
{
- boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock);
- if (--m_decrypt_keys_lockers) // another lock left ?
- return;
m_account.encrypt_keys(key);
m_account.decrypt_viewkey(key);
}
void wallet2::decrypt_keys(const crypto::chacha_key &key)
{
- boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock);
- if (m_decrypt_keys_lockers++) // already unlocked ?
- return;
m_account.encrypt_viewkey(key);
m_account.decrypt_keys(key);
}
@@ -4768,7 +4742,6 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
memwipe(&skey, sizeof(rct::key));
m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
- m_account.finalize_multisig(spend_public_key);
// Not possible to restore a multisig wallet that is able to activate the MMS
// (because the original keys are not (yet) part of the restore info), so
@@ -4983,24 +4956,12 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
store();
}
}
-
+//----------------------------------------------------------------------------------------------------
std::string wallet2::make_multisig(const epee::wipeable_string &password,
- const std::vector<crypto::secret_key> &view_keys,
- const std::vector<crypto::public_key> &spend_keys,
- uint32_t threshold)
+ const std::vector<std::string> &initial_kex_msgs,
+ const std::uint32_t threshold)
{
- CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
- CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
- CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
-
- std::string extra_multisig_info;
- std::vector<crypto::secret_key> multisig_keys;
- rct::key spend_pkey = rct::identity();
- rct::key spend_skey;
- auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(&spend_skey, sizeof(spend_skey));});
- std::vector<crypto::public_key> multisig_signers;
-
- // decrypt keys
+ // decrypt account keys
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
@@ -5008,104 +4969,89 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
m_account.encrypt_viewkey(chacha_key);
m_account.decrypt_keys(chacha_key);
- keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler(
+ [&, this, chacha_key]()
+ {
+ m_account.encrypt_keys(chacha_key);
+ m_account.decrypt_viewkey(chacha_key);
+ }
+ );
}
- // In common multisig scheme there are 4 types of key exchange rounds:
- // 1. First round is exchange of view secret keys and public spend keys.
- // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
- // M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
- // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
- // k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
- // And secret spend key as the sum of all participant's secret multisig keys
- // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
- // and calculate common spend public key as sum of all unique participants' public multisig keys.
- // Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
-
- // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
- // Wallet's public spend key is the sum of unique public multisig keys of all participants.
- // secret_spend_key * G = public signer key
-
- if (threshold == spend_keys.size() + 1)
- {
- // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
- MINFO("Creating spend key...");
+ // create multisig account
+ multisig::multisig_account multisig_account{
+ multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key),
+ multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key)
+ };
- // Calculates all multisig keys and spend key
- cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
+ // open initial kex messages, validate them, extract signers
+ std::vector<multisig::multisig_kex_msg> expanded_msgs;
+ std::vector<crypto::public_key> signers;
+ expanded_msgs.reserve(initial_kex_msgs.size());
+ signers.reserve(initial_kex_msgs.size() + 1);
- // Our signer key is b * G, where b is secret spend key.
- multisig_signers = spend_keys;
- multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
- }
- else
+ for (const auto &msg : initial_kex_msgs)
{
- // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
- // note that derivations are public keys as DH exchange suppose it to be
- auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
+ expanded_msgs.emplace_back(msg);
- spend_pkey = rct::identity();
- multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
+ // validate each message
+ // 1. must be 'round 1'
+ CHECK_AND_ASSERT_THROW_MES(expanded_msgs.back().get_round() == 1,
+ "Trying to make multisig with message that has invalid multisig kex round (should be '1').");
- if (threshold == spend_keys.size())
- {
- // N - 1 / N case
+ // 2. duplicate signers not allowed
+ CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), expanded_msgs.back().get_signing_pubkey()) == signers.end(),
+ "Duplicate signers not allowed when converting a wallet to multisig.");
- // We need an extra step, so we package all the composite public keys
- // we know about, and make a signed string out of them
- MINFO("Creating spend key...");
+ // add signer (skip self for now)
+ if (expanded_msgs.back().get_signing_pubkey() != multisig_account.get_base_pubkey())
+ signers.push_back(expanded_msgs.back().get_signing_pubkey());
+ }
- // Calculating set of our secret multisig keys as follows: mi = H(Mi),
- // where mi - secret multisig key, Mi - others' participants public multisig key
- multisig_keys = cryptonote::calculate_multisig_keys(derivations);
+ // add self to signers
+ signers.push_back(multisig_account.get_base_pubkey());
- // calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
- // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
- // Entire wallet's secret spend is sum of all unique secret multisig keys
- // among all of participants and is not held by anyone!
- spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
+ // intialize key exchange
+ multisig_account.initialize_kex(threshold, signers, expanded_msgs);
+ CHECK_AND_ASSERT_THROW_MES(multisig_account.account_is_active(), "Failed to activate multisig account.");
- // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
- extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
- }
- else
- {
- // M / N case
- MINFO("Preparing keys for next exchange round...");
-
- // Preparing data for middle round - packing new public multisig keys to exchage with others.
- extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
- spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
-
- // Need to store middle keys to be able to proceed in case of wallet shutdown.
- m_multisig_derivations = derivations;
- }
- }
-
+ // update wallet state
if (!m_original_keys_available)
{
// Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
// (making a wallet multisig overwrites those keys, see account_base::make_multisig)
- m_original_address = m_account.get_keys().m_account_address;
- m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
+ m_original_address = get_account().get_keys().m_account_address;
+ m_original_view_secret_key = get_account().get_keys().m_view_secret_key;
m_original_keys_available = true;
}
clear();
- MINFO("Creating view key...");
- crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
+ // account base
MINFO("Creating multisig address...");
- CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
- "Failed to create multisig wallet due to bad keys");
- memwipe(&spend_skey, sizeof(rct::key));
+ CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(multisig_account.get_common_privkey(),
+ multisig_account.get_base_privkey(),
+ multisig_account.get_multisig_pubkey(),
+ multisig_account.get_multisig_privkeys()),
+ "Failed to create multisig wallet account due to bad keys");
init_type(hw::device::device_type::SOFTWARE);
m_original_keys_available = true;
m_multisig = true;
m_multisig_threshold = threshold;
- m_multisig_signers = multisig_signers;
- ++m_multisig_rounds_passed;
+ m_multisig_signers = signers;
+ m_multisig_rounds_passed = 1;
+
+ // derivations stored (should be empty in last round)
+ // TODO: make use of the origins map for aggregation-style signing (instead of round-robin)
+ m_multisig_derivations.clear();
+ m_multisig_derivations.reserve(multisig_account.get_kex_keys_to_origins_map().size());
+
+ for (const auto &key_to_origins : multisig_account.get_kex_keys_to_origins_map())
+ m_multisig_derivations.push_back(key_to_origins.first);
+
+ // address
+ m_account_public_address.m_spend_public_key = multisig_account.get_multisig_pubkey();
// re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
@@ -5118,42 +5064,18 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
if (!m_wallet_file.empty())
store();
- return extra_multisig_info;
-}
-
-std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
- const std::vector<std::string> &info)
-{
- THROW_WALLET_EXCEPTION_IF(info.empty(),
- error::wallet_internal_error, "Empty multisig info");
-
- if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
- {
- THROW_WALLET_EXCEPTION_IF(false,
- error::wallet_internal_error, "Unsupported info string");
- }
-
- std::vector<crypto::public_key> signers;
- std::unordered_set<crypto::public_key> pkeys;
-
- THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
- error::wallet_internal_error, "Bad extra multisig info");
-
- return exchange_multisig_keys(password, pkeys, signers);
+ return multisig_account.get_next_kex_round_msg();
}
-
+//----------------------------------------------------------------------------------------------------
std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
- std::unordered_set<crypto::public_key> derivations,
- std::vector<crypto::public_key> signers)
+ const std::vector<std::string> &kex_messages)
{
- CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
- CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
-
- bool ready = false;
+ bool ready{false};
CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
+ CHECK_AND_ASSERT_THROW_MES(kex_messages.size() > 0, "No key exchange messages passed in.");
- // keys are decrypted
+ // decrypt account keys
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
@@ -5161,37 +5083,72 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
m_account.encrypt_viewkey(chacha_key);
m_account.decrypt_keys(chacha_key);
- keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
- }
-
- if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
- {
- // the last round is passed and we have to calculate spend public key
- // add ours if not included
- crypto::public_key local_signer = get_multisig_signer_public_key();
-
- if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
- {
- signers.push_back(local_signer);
- for (const auto &msk: get_account().get_multisig_keys())
+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler(
+ [&, this, chacha_key]()
{
- derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
+ m_account.encrypt_keys(chacha_key);
+ m_account.decrypt_viewkey(chacha_key);
}
- }
+ );
+ }
+
+ // open kex messages
+ std::vector<multisig::multisig_kex_msg> expanded_msgs;
+ expanded_msgs.reserve(kex_messages.size());
+
+ for (const auto &msg : kex_messages)
+ expanded_msgs.emplace_back(msg);
+
+ // reconstruct multisig account
+ crypto::public_key dummy;
+ multisig::multisig_account::kex_origins_map_t kex_origins_map;
+
+ for (const auto &derivation : m_multisig_derivations)
+ kex_origins_map[derivation];
+
+ multisig::multisig_account multisig_account{
+ m_multisig_threshold,
+ m_multisig_signers,
+ get_account().get_keys().m_spend_secret_key,
+ crypto::null_skey, //base common privkey: not used
+ get_account().get_keys().m_multisig_keys,
+ get_account().get_keys().m_view_secret_key,
+ m_account_public_address.m_spend_public_key,
+ dummy, //common pubkey: not used
+ m_multisig_rounds_passed,
+ std::move(kex_origins_map),
+ ""
+ };
- CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
+ // update multisig kex
+ multisig_account.kex_update(expanded_msgs);
- // Summing all of unique public multisig keys to calculate common public spend key
- crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
- m_account_public_address.m_spend_public_key = spend_public_key;
- m_account.finalize_multisig(spend_public_key);
+ // update wallet state
- m_multisig_signers = signers;
- std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)) < 0; });
+ // address
+ m_account_public_address.m_spend_public_key = multisig_account.get_multisig_pubkey();
- ++m_multisig_rounds_passed;
- m_multisig_derivations.clear();
+ // account base
+ CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(multisig_account.get_common_privkey(),
+ multisig_account.get_base_privkey(),
+ multisig_account.get_multisig_pubkey(),
+ multisig_account.get_multisig_privkeys()),
+ "Failed to update multisig wallet account due to bad keys");
+
+ // derivations stored (should be empty in last round)
+ // TODO: make use of the origins map for aggregation-style signing (instead of round-robin)
+ m_multisig_derivations.clear();
+ m_multisig_derivations.reserve(multisig_account.get_kex_keys_to_origins_map().size());
+
+ for (const auto &key_to_origins : multisig_account.get_kex_keys_to_origins_map())
+ m_multisig_derivations.push_back(key_to_origins.first);
+ // rounds passed
+ m_multisig_rounds_passed = multisig_account.get_kex_rounds_complete();
+
+ // why is this necessary? who knows...
+ if (multisig_account.multisig_is_ready())
+ {
// keys are encrypted again
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
@@ -5213,270 +5170,28 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
if (!m_wallet_file.empty())
store();
-
- return {};
}
- // Below are either middle or secret spend key establishment rounds
-
- for (const auto& key: m_multisig_derivations)
- derivations.erase(key);
-
- // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
- auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
-
- std::string extra_multisig_info;
- if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
- {
- // Next round is last therefore we are performing secret spend establishment round as described above.
- MINFO("Creating spend key...");
-
- // Calculating our secret multisig keys by hashing our public multisig keys.
- auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
- // And summing it to get personal secret spend key
- crypto::secret_key spend_skey = cryptonote::calculate_multisig_signer_key(multisig_keys);
-
- m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
-
- // Packing public multisig keys to exchange with others and calculate common public spend key in the last round
- extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
- }
- else
- {
- // This is just middle round
- MINFO("Preparing keys for next exchange round...");
- extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
- m_multisig_derivations = new_derivations;
- }
-
- ++m_multisig_rounds_passed;
-
+ // wallet/file relationship
if (!m_wallet_file.empty())
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
- return extra_multisig_info;
-}
-
-void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
- std::vector<crypto::public_key> &public_keys,
- std::vector<crypto::secret_key> &secret_keys) const
-{
- // parse all multisig info
- public_keys.resize(info.size());
- secret_keys.resize(info.size());
- for (size_t i = 0; i < info.size(); ++i)
- {
- THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
- error::wallet_internal_error, "Bad multisig info: " + info[i]);
- }
-
- // remove duplicates
- for (size_t i = 0; i < secret_keys.size(); ++i)
- {
- for (size_t j = i + 1; j < secret_keys.size(); ++j)
- {
- if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
- {
- MDEBUG("Duplicate key found, ignoring");
- secret_keys[j] = secret_keys.back();
- public_keys[j] = public_keys.back();
- secret_keys.pop_back();
- public_keys.pop_back();
- --j;
- }
- }
- }
-
- // people may include their own, weed it out
- const crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
- const crypto::public_key local_pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
- for (size_t i = 0; i < secret_keys.size(); ++i)
- {
- if (secret_keys[i] == local_skey)
- {
- MDEBUG("Local key is present, ignoring");
- secret_keys[i] = secret_keys.back();
- public_keys[i] = public_keys.back();
- secret_keys.pop_back();
- public_keys.pop_back();
- --i;
- }
- else
- {
- THROW_WALLET_EXCEPTION_IF(public_keys[i] == local_pkey, error::wallet_internal_error,
- "Found local spend public key, but not local view secret key - something very weird");
- }
- }
-}
-
-std::string wallet2::make_multisig(const epee::wipeable_string &password,
- const std::vector<std::string> &info,
- uint32_t threshold)
-{
- std::vector<crypto::secret_key> secret_keys(info.size());
- std::vector<crypto::public_key> public_keys(info.size());
- unpack_multisig_info(info, public_keys, secret_keys);
- return make_multisig(password, secret_keys, public_keys, threshold);
-}
-
-bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
-{
- bool ready;
- uint32_t threshold, total;
- if (!multisig(&ready, &threshold, &total))
- {
- MERROR("This is not a multisig wallet");
- return false;
- }
- if (ready)
- {
- MERROR("This multisig wallet is already finalized");
- return false;
- }
- if (threshold + 1 != total)
- {
- MERROR("finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
- return false;
- }
- exchange_multisig_keys(password, pkeys, signers);
- return true;
-}
-
-bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
- std::vector<crypto::public_key> &signers,
- std::unordered_set<crypto::public_key> &pkeys) const
-{
- // parse all multisig info
- signers.resize(info.size(), crypto::null_pkey);
- for (size_t i = 0; i < info.size(); ++i)
- {
- if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
- {
- return false;
- }
- }
-
- return true;
-}
-
-bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
-{
- std::unordered_set<crypto::public_key> public_keys;
- std::vector<crypto::public_key> signers;
- if (!unpack_extra_multisig_info(info, signers, public_keys))
- {
- MERROR("Bad multisig info");
- return false;
- }
-
- return finalize_multisig(password, public_keys, signers);
-}
-
-std::string wallet2::get_multisig_info() const
-{
- // It's a signed package of private view key and public spend key
- const crypto::secret_key skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
- const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
- crypto::hash hash;
-
- std::string data;
- data += std::string((const char *)&skey, sizeof(crypto::secret_key));
- data += std::string((const char *)&pkey, sizeof(crypto::public_key));
-
- data.resize(data.size() + sizeof(crypto::signature));
- crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
- crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
- crypto::generate_signature(hash, pkey, get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key), signature);
-
- return std::string("MultisigV1") + tools::base58::encode(data);
-}
-
-bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey)
-{
- const size_t header_len = strlen("MultisigV1");
- if (data.size() < header_len || data.substr(0, header_len) != "MultisigV1")
- {
- MERROR("Multisig info header check error");
- return false;
- }
- std::string decoded;
- if (!tools::base58::decode(data.substr(header_len), decoded))
- {
- MERROR("Multisig info decoding error");
- return false;
- }
- if (decoded.size() != sizeof(crypto::secret_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))
- {
- MERROR("Multisig info is corrupt");
- return false;
- }
-
- size_t offset = 0;
- skey = *(const crypto::secret_key*)(decoded.data() + offset);
- offset += sizeof(skey);
- pkey = *(const crypto::public_key*)(decoded.data() + offset);
- offset += sizeof(pkey);
- const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset);
-
- crypto::hash hash;
- crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
- if (!crypto::check_signature(hash, pkey, signature))
- {
- MERROR("Multisig info signature is invalid");
- return false;
- }
-
- return true;
+ return multisig_account.get_next_kex_round_msg();
}
-
-bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::get_multisig_first_kex_msg() const
{
- if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
- {
- MERROR("Multisig info header check error");
- return false;
- }
- std::string decoded;
- if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
- {
- MERROR("Multisig info decoding error");
- return false;
- }
- if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::signature))
- {
- MERROR("Multisig info is corrupt");
- return false;
- }
- if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
- {
- MERROR("Multisig info is corrupt");
- return false;
- }
-
- const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
- size_t offset = 0;
- signer = *(const crypto::public_key*)(decoded.data() + offset);
- offset += sizeof(signer);
- const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
-
- crypto::hash hash;
- crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
- if (!crypto::check_signature(hash, signer, signature))
- {
- MERROR("Multisig info signature is invalid");
- return false;
- }
-
- for (size_t n = 0; n < n_keys; ++n)
- {
- crypto::public_key mspk = *(const crypto::public_key*)(decoded.data() + offset);
- pkeys.insert(mspk);
- offset += sizeof(mspk);
- }
+ // create multisig account
+ multisig::multisig_account multisig_account{
+ // k_base = H(normal private spend key)
+ multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key),
+ // k_view = H(normal private view key)
+ multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key)
+ };
- return true;
+ return multisig_account.get_next_kex_round_msg();
}
-
+//----------------------------------------------------------------------------------------------------
bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
{
if (!m_multisig)
@@ -5489,7 +5204,7 @@ bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
*ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
return true;
}
-
+//----------------------------------------------------------------------------------------------------
bool wallet2::has_multisig_partial_key_images() const
{
if (!m_multisig)
@@ -5499,7 +5214,7 @@ bool wallet2::has_multisig_partial_key_images() const
return true;
return false;
}
-
+//----------------------------------------------------------------------------------------------------
bool wallet2::has_unknown_key_images() const
{
for (const auto &td: m_transfers)
@@ -5714,13 +5429,14 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
//keys loaded ok!
- //try to load wallet file. but even if we failed, it is not big problem
- if (use_fs && (!boost::filesystem::exists(m_wallet_file, e) || e))
+ //try to load wallet cache. but even if we failed, it is not big problem
+ bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty();
+ if (cache_missing)
{
- LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
+ LOG_PRINT_L0("wallet cache missing: " << m_wallet_file << ", starting with empty blockchain");
m_account_public_address = m_account.get_keys().m_account_address;
}
- else if (use_fs || !cache_buf.empty())
+ else
{
wallet2::cache_file_data cache_file_data;
std::string cache_file_buf;
@@ -6130,6 +5846,19 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
amount_per_subaddr[0] = utx.second.m_change;
else
found->second += utx.second.m_change;
+
+ // add transfers to same wallet
+ for (const auto &dest: utx.second.m_dests) {
+ auto index = get_subaddress_index(dest.addr);
+ if (index && (*index).major == index_major)
+ {
+ auto found = amount_per_subaddr.find((*index).minor);
+ if (found == amount_per_subaddr.end())
+ amount_per_subaddr[(*index).minor] = dest.amount;
+ else
+ found->second += dest.amount;
+ }
+ }
}
}
@@ -7077,7 +6806,6 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func)
{
std::string s = signed_tx_st;
- boost::system::error_code errcode;
signed_tx_set signed_txs;
const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
@@ -7501,16 +7229,16 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
{
if (use_per_byte_fee)
{
- const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
}
else
{
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
}
}
@@ -9231,8 +8959,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
ptx.construction_data.rct_config = {
- tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
- use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1
+ rct::RangeProofPaddedBulletproof,
+ use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 : 3
};
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
@@ -9927,10 +9655,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
const uint64_t base_fee = get_base_fee();
@@ -9966,7 +9695,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
- const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag));
+ const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus));
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -9984,8 +9713,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
@@ -10082,7 +9811,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -10195,7 +9924,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -10212,7 +9941,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
+ if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() &&
+ estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -10250,7 +9980,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
}
@@ -10261,7 +9991,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
{
@@ -10521,11 +10251,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
// determine threshold for fractional amount
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
@@ -10631,10 +10362,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -10663,7 +10395,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
uint64_t fee_dust_threshold;
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
{
- const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
+ const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
}
else
@@ -10694,7 +10426,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_weight_limit);
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag, bulletproof_plus);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) {
@@ -10702,7 +10434,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
// add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
@@ -11564,8 +11296,10 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::secret_key scalar1;
crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
- const rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ rct::key C = tx.rct_signatures.outPk[n].mask;
+ if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
+ C = rct::scalarmult8(C);
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
@@ -12217,7 +11951,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -13323,13 +13057,6 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
return imported_outputs;
}
//----------------------------------------------------------------------------------------------------
-crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
-{
- crypto::public_key pkey;
- crypto::secret_key_to_public_key(get_multisig_blinded_secret_key(spend_skey), pkey);
- return pkey;
-}
-//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_multisig_signer_public_key() const
{
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
@@ -13373,7 +13100,7 @@ rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) con
CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index");
rct::multisig_kLRki kLRki;
kLRki.k = k;
- cryptonote::generate_multisig_LR(m_transfers[n].get_public_key(), rct::rct2sk(kLRki.k), (crypto::public_key&)kLRki.L, (crypto::public_key&)kLRki.R);
+ multisig::generate_multisig_LR(m_transfers[n].get_public_key(), rct::rct2sk(kLRki.k), (crypto::public_key&)kLRki.L, (crypto::public_key&)kLRki.R);
kLRki.ki = rct::ki2rct(m_transfers[n].m_key_image);
return kLRki;
}
@@ -13420,7 +13147,7 @@ crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
for (const auto &info: td.m_multisig_info)
for (const auto &pki: info.m_partial_key_images)
pkis.push_back(pki);
- bool r = cryptonote::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki);
+ bool r = multisig::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
return ki;
}
@@ -13443,7 +13170,7 @@ cryptonote::blobdata wallet2::export_multisig()
for (size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
{
// we want to export the partial key image, not the full one, so we can't use td.m_key_image
- bool r = generate_multisig_key_image(get_account().get_keys(), m, td.get_public_key(), ki);
+ bool r = multisig::generate_multisig_key_image(get_account().get_keys(), m, td.get_public_key(), ki);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate key image");
info[n].m_partial_key_images.push_back(ki);
}
@@ -13734,12 +13461,8 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay
if (!payment_id.empty())
{
- crypto::hash pid32;
- if (!wallet2::parse_long_payment_id(payment_id, pid32))
- {
- error = "Invalid payment id";
- return std::string();
- }
+ error = "Standalone payment id deprecated, use integrated address instead";
+ return std::string();
}
std::string uri = "monero:" + address;
@@ -14362,9 +14085,10 @@ std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, i
n_outputs = 2; // extra dummy output
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
- size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
- uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
+ size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
+ uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return std::make_pair(size, weight);
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index facf9878d..ccf9a96a3 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -757,45 +757,20 @@ private:
* to other participants
*/
std::string make_multisig(const epee::wipeable_string &password,
- const std::vector<std::string> &info,
- uint32_t threshold);
+ const std::vector<std::string> &kex_messages,
+ const std::uint32_t threshold);
/*!
- * \brief Creates a multisig wallet
+ * \brief Increment the multisig key exchange round
* \return empty if done, non empty if we need to send another string
* to other participants
*/
- std::string make_multisig(const epee::wipeable_string &password,
- const std::vector<crypto::secret_key> &view_keys,
- const std::vector<crypto::public_key> &spend_keys,
- uint32_t threshold);
std::string exchange_multisig_keys(const epee::wipeable_string &password,
- const std::vector<std::string> &info);
- /*!
- * \brief Any but first round of keys exchange
- */
- std::string exchange_multisig_keys(const epee::wipeable_string &password,
- std::unordered_set<crypto::public_key> pkeys,
- std::vector<crypto::public_key> signers);
- /*!
- * \brief Finalizes creation of a multisig wallet
- */
- bool finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info);
- /*!
- * \brief Finalizes creation of a multisig wallet
- */
- bool finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers);
- /*!
- * Get a packaged multisig information string
- */
- std::string get_multisig_info() const;
- /*!
- * Verifies and extracts keys from a packaged multisig information string
- */
- static bool verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey);
+ const std::vector<std::string> &kex_messages);
/*!
- * Verifies and extracts keys from a packaged multisig information string
+ * \brief Get initial message to start multisig key exchange (before 'make_multisig()' is called)
+ * \return string to send to other participants
*/
- static bool verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer);
+ std::string get_multisig_first_kex_msg() const;
/*!
* Export multisig info
* This will generate and remember new k values
@@ -1229,6 +1204,8 @@ private:
void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; }
bool track_uses() const { return m_track_uses; }
void track_uses(bool value) { m_track_uses = value; }
+ bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; }
+ void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; }
BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; }
void setup_background_mining(BackgroundMiningSetupType value) { m_setup_background_mining = value; }
uint32_t inactivity_lock_timeout() const { return m_inactivity_lock_timeout; }
@@ -1411,7 +1388,7 @@ private:
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
- uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
+ uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
uint64_t get_base_fee();
uint64_t get_fee_quantization_mask();
@@ -1477,7 +1454,6 @@ private:
void set_attribute(const std::string &key, const std::string &value);
bool get_attribute(const std::string &key, std::string &value) const;
- crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const;
crypto::public_key get_multisig_signer_public_key() const;
crypto::public_key get_multisig_signing_public_key(size_t idx) const;
crypto::public_key get_multisig_signing_public_key(const crypto::secret_key &skey) const;
@@ -1641,12 +1617,6 @@ private:
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
- void unpack_multisig_info(const std::vector<std::string>& info,
- std::vector<crypto::public_key> &public_keys,
- std::vector<crypto::secret_key> &secret_keys) const;
- bool unpack_extra_multisig_info(const std::vector<std::string>& info,
- std::vector<crypto::public_key> &signers,
- std::unordered_set<crypto::public_key> &pkeys) const;
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> create_output_tracker_cache() const;
@@ -1749,6 +1719,7 @@ private:
uint64_t m_ignore_outputs_above;
uint64_t m_ignore_outputs_below;
bool m_track_uses;
+ bool m_show_wallet_name_when_locked;
uint32_t m_inactivity_lock_timeout;
BackgroundMiningSetupType m_setup_background_mining;
bool m_persistent_rpc_client_id;
@@ -1798,9 +1769,7 @@ private:
crypto::secret_key m_original_view_secret_key;
crypto::chacha_key m_cache_key;
- boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
- boost::mutex m_decrypt_keys_lock;
- unsigned int m_decrypt_keys_lockers;
+ std::shared_ptr<wallet_keys_unlocker> m_encrypt_keys_after_refresh;
bool m_unattended;
bool m_devices_registered;
diff --git a/src/wallet/wallet_rpc_helpers.h b/src/wallet/wallet_rpc_helpers.h
index 35714db03..6f50b6727 100644
--- a/src/wallet/wallet_rpc_helpers.h
+++ b/src/wallet/wallet_rpc_helpers.h
@@ -28,6 +28,7 @@
#pragma once
+#include <limits>
#include <type_traits>
namespace
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 4655e24cd..a173b5a50 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -3938,7 +3938,7 @@ namespace tools
return false;
}
- res.multisig_info = m_wallet->get_multisig_info();
+ res.multisig_info = m_wallet->get_multisig_first_kex_msg();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -4069,7 +4069,7 @@ namespace tools
catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Error calling import_multisig";
+ er.message = std::string{"Error calling import_multisig: "} + e.what();
return false;
}
@@ -4094,53 +4094,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- bool ready;
- uint32_t threshold, total;
- if (!m_wallet->multisig(&ready, &threshold, &total))
- {
- er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
- er.message = "This wallet is not multisig";
- return false;
- }
- if (ready)
- {
- er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
- er.message = "This wallet is multisig, and already finalized";
- return false;
- }
-
- if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
- {
- er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
- er.message = "Needs multisig info from more participants";
- return false;
- }
-
- try
- {
- if (!m_wallet->finalize_multisig(req.password, req.multisig_info))
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Error calling finalize_multisig";
- return false;
- }
- }
- catch (const std::exception &e)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = std::string("Error calling finalize_multisig: ") + e.what();
- return false;
- }
- res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
-
- return true;
+ return false;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
@@ -4168,7 +4122,7 @@ namespace tools
return false;
}
- if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
+ if (req.multisig_info.size() + 1 < total)
{
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
er.message = "Needs multisig info from more participants";
@@ -4426,7 +4380,11 @@ namespace tools
return false;
}
- if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
+ boost::optional<epee::net_utils::http::login> daemon_login{};
+ if (!req.username.empty() || !req.password.empty())
+ daemon_login.emplace(req.username, req.password);
+
+ if (!m_wallet->set_daemon(req.address, daemon_login, req.trusted, std::move(ssl_options)))
{
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
er.message = std::string("Unable to set daemon");
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 248d31aa4..867ea54bd 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 23
+#define WALLET_RPC_VERSION_MINOR 24
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -2504,24 +2504,17 @@ namespace wallet_rpc
struct COMMAND_RPC_FINALIZE_MULTISIG
{
+ // NOP
struct request_t
{
- std::string password;
- std::vector<std::string> multisig_info;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(password)
- KV_SERIALIZE(multisig_info)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
- std::string address;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2664,6 +2657,8 @@ namespace wallet_rpc
struct request_t
{
std::string address;
+ std::string username;
+ std::string password;
bool trusted;
std::string ssl_support; // disabled, enabled, autodetect
std::string ssl_private_key_path;
@@ -2674,6 +2669,8 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
+ KV_SERIALIZE(username)
+ KV_SERIALIZE(password)
KV_SERIALIZE_OPT(trusted, false)
KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect")
KV_SERIALIZE(ssl_private_key_path)