aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp284
1 files changed, 262 insertions, 22 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index bbb801716..2080c1832 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -222,10 +222,11 @@ struct options {
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
+ const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
};
-void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
+void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
{
keys_file = file_path;
wallet_file = file_path;
@@ -237,6 +238,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
{//provided wallet file name
keys_file += ".keys";
}
+ mms_file = file_path + ".mms";
}
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
@@ -274,6 +276,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
auto device_name = command_line::get_arg(vm, opts.hw_device);
+ auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -328,7 +331,9 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
+ wallet->get_message_store().set_options(vm);
wallet->device_name(device_name);
+ wallet->device_derivation_path(device_derivation_path);
try
{
@@ -838,6 +843,24 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
}
}
+void wallet_device_callback::on_button_request()
+{
+ if (wallet)
+ wallet->on_button_request();
+}
+
+void wallet_device_callback::on_pin_request(epee::wipeable_string & pin)
+{
+ if (wallet)
+ wallet->on_pin_request(pin);
+}
+
+void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
+{
+ if (wallet)
+ wallet->on_passphrase_request(on_device, passphrase);
+}
+
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
@@ -871,6 +894,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_key_reuse_mitigation2(true),
m_segregation_height(0),
m_ignore_fractional_outputs(true),
+ m_track_uses(false),
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
@@ -886,12 +910,15 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_light_wallet_connected(false),
m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0),
+ m_original_keys_available(false),
+ m_message_store(),
m_key_device_type(hw::device::device_type::SOFTWARE),
m_ring_history_saved(false),
m_ringdb(),
m_last_block_reward(0),
m_encrypt_keys_after_refresh(boost::none),
- m_unattended(unattended)
+ m_unattended(unattended),
+ m_device_last_key_image_sync(0)
{
}
@@ -914,6 +941,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_
return command_line::get_arg(vm, options().hw_device);
}
+std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
+{
+ return command_line::get_arg(vm, options().hw_device_derivation_path);
+}
+
void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
@@ -929,7 +961,9 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
+ mms::message_store::init_options(desc_params);
command_line::add_arg(desc_params, opts.hw_device);
+ command_line::add_arg(desc_params, opts.hw_device_derivation_path);
command_line::add_arg(desc_params, opts.tx_notify);
}
@@ -949,7 +983,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
return {nullptr, password_container{}};
}
auto wallet = make_basic(vm, unattended, opts, password_prompter);
- if (wallet)
+ if (wallet && !wallet_file.empty())
{
wallet->load(wallet_file, pwd->password());
}
@@ -1091,15 +1125,17 @@ bool wallet2::reconnect_device()
hw::device &hwdev = lookup_device(m_device_name);
hwdev.set_name(m_device_name);
hwdev.set_network_type(m_nettype);
+ hwdev.set_derivation_path(m_device_derivation_path);
+ hwdev.set_callback(get_device_callback());
r = hwdev.init();
if (!r){
- LOG_PRINT_L2("Could not init device");
+ MERROR("Could not init device");
return false;
}
r = hwdev.connect();
if (!r){
- LOG_PRINT_L2("Could not connect to the device");
+ MERROR("Could not connect to the device");
return false;
}
@@ -1411,8 +1447,9 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
+ PERF_TIMER(process_new_transaction);
// In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
@@ -1649,6 +1686,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if (!m_multisig && !m_watch_only)
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
+ if (output_tracker_cache)
+ (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = m_transfers.size() - 1;
if (m_multisig)
{
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
@@ -1714,6 +1753,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_mask = rct::identity();
td.m_rct = false;
}
+ if (output_tracker_cache)
+ (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = kit->second;
if (m_multisig)
{
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
@@ -1745,11 +1786,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
if(in.type() != typeid(cryptonote::txin_to_key))
continue;
- auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
+ const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in);
+ auto it = m_key_images.find(in_to_key.k_image);
if(it != m_key_images.end())
{
transfer_details& td = m_transfers[it->second];
- uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
+ uint64_t amount = in_to_key.amount;
if (amount > 0)
{
if(amount != td.amount())
@@ -1780,6 +1822,34 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
}
}
+
+ if (!pool && m_track_uses)
+ {
+ PERF_TIMER(track_uses);
+ const uint64_t amount = in_to_key.amount;
+ std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets);
+ if (output_tracker_cache)
+ {
+ for (uint64_t offset: offsets)
+ {
+ const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->find(std::make_pair(amount, offset));
+ if (i != output_tracker_cache->end())
+ {
+ size_t idx = i->second;
+ THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range");
+ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
+ }
+ }
+ }
+ else for (transfer_details &td: m_transfers)
+ {
+ if (amount != in_to_key.amount)
+ continue;
+ for (uint64_t offset: offsets)
+ if (offset == td.m_global_output_index)
+ td.m_uses.push_back(std::make_pair(height, txid));
+ }
+ }
}
uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
@@ -1962,7 +2032,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
add_rings(tx);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset)
+void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
"block transactions=" + std::to_string(bche.txs.size()) +
@@ -1975,7 +2045,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
{
TIME_MEASURE_START(miner_tx_handle_time);
if (m_refresh_type != RefreshNoCoinbase)
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset]);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
++tx_cache_data_offset;
TIME_MEASURE_FINISH(miner_tx_handle_time);
@@ -1984,7 +2054,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
{
- process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]);
+ process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
}
TIME_MEASURE_FINISH(txs_handle_time);
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
@@ -2082,7 +2152,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
hashes = std::move(res.m_block_ids);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added)
+void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
size_t current_index = start_height;
blocks_added = 0;
@@ -2189,7 +2259,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
if(current_index >= m_blockchain.size())
{
- process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
++blocks_added;
}
else if(bl_id != m_blockchain[current_index])
@@ -2201,7 +2271,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index);
- process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
}
else
{
@@ -2634,6 +2704,17 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
}
//----------------------------------------------------------------------------------------------------
+std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create_output_tracker_cache() const
+{
+ std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> cache{new std::map<std::pair<uint64_t, uint64_t>, size_t>()};
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details &td = m_transfers[i];
+ (*cache)[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
+ }
+ return cache;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
{
if(m_light_wallet) {
@@ -2680,6 +2761,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
std::vector<cryptonote::block_complete_entry> blocks;
std::vector<parsed_block> parsed_blocks;
bool refreshed = false;
+ std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache;
// pull the first set of blocks
get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
@@ -2733,7 +2815,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
try
{
- process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks);
+ process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks, output_tracker_cache.get());
}
catch (const tools::error::out_of_hashchain_bounds_error&)
{
@@ -2776,6 +2858,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
throw std::runtime_error("proxy exception in refresh thread");
}
+ // if we've got at least 10 blocks to refresh, assume we're starting
+ // a long refresh, and setup a tracking output cache if we need to
+ if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10)
+ output_tracker_cache = create_output_tracker_cache();
+
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
blocks = std::move(next_blocks);
@@ -2998,6 +3085,7 @@ bool wallet2::clear()
m_subaddresses.clear();
m_subaddress_labels.clear();
m_multisig_rounds_passed = 0;
+ m_device_last_key_image_sync = 0;
return true;
}
@@ -3147,18 +3235,39 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
+ value2.SetInt(m_track_uses ? 1 : 0);
+ json.AddMember("track_uses", value2, json.GetAllocator());
+
value2.SetUint(m_subaddress_lookahead_major);
json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
value2.SetUint(m_subaddress_lookahead_minor);
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
+ value2.SetInt(m_original_keys_available ? 1 : 0);
+ json.AddMember("original_keys_available", value2, json.GetAllocator());
+
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
value.SetString(m_device_name.c_str(), m_device_name.size());
json.AddMember("device_name", value, json.GetAllocator());
+ value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
+ json.AddMember("device_derivation_path", value, json.GetAllocator());
+
+ std::string original_address;
+ std::string original_view_secret_key;
+ if (m_original_keys_available)
+ {
+ original_address = get_account_address_as_str(m_nettype, false, m_original_address);
+ value.SetString(original_address.c_str(), original_address.length());
+ json.AddMember("original_address", value, json.GetAllocator());
+ original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
+ value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
+ json.AddMember("original_view_secret_key", value, json.GetAllocator());
+ }
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -3275,9 +3384,12 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_key_reuse_mitigation2 = true;
m_segregation_height = 0;
m_ignore_fractional_outputs = true;
+ m_track_uses = false;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
+ m_original_keys_available = false;
m_device_name = "";
+ m_device_derivation_path = "";
m_key_device_type = hw::device::device_type::SOFTWARE;
encrypted_secret_keys = false;
}
@@ -3425,6 +3537,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = field_segregation_height;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
m_ignore_fractional_outputs = field_ignore_fractional_outputs;
+ 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, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
@@ -3445,6 +3559,38 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
}
}
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
+ m_device_derivation_path = field_device_derivation_path;
+
+ if (json.HasMember("original_keys_available"))
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
+ m_original_keys_available = field_original_keys_available;
+ if (m_original_keys_available)
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, true, std::string());
+ address_parse_info info;
+ bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
+ if (!ok)
+ {
+ LOG_ERROR("Failed to parse original_address from JSON");
+ return false;
+ }
+ m_original_address = info.address;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, true, std::string());
+ ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
+ if (!ok)
+ {
+ LOG_ERROR("Failed to parse original_view_secret_key from JSON");
+ return false;
+ }
+ }
+ }
+ else
+ {
+ m_original_keys_available = false;
+ }
}
else
{
@@ -3459,6 +3605,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
hw::device &hwdev = lookup_device(m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
hwdev.set_network_type(m_nettype);
+ hwdev.set_derivation_path(m_device_derivation_path);
+ hwdev.set_callback(get_device_callback());
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
m_account.set_device(hwdev);
@@ -3769,6 +3917,10 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
+ // 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)
+ m_original_keys_available = false;
+
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
setup_new_blockchain();
@@ -3806,6 +3958,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ m_original_keys_available = false;
m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
@@ -3894,6 +4047,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ m_original_keys_available = false;
m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
@@ -3934,6 +4088,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ m_original_keys_available = false;
m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
@@ -3965,6 +4120,8 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
auto &hwdev = lookup_device(device_name);
hwdev.set_name(device_name);
hwdev.set_network_type(m_nettype);
+ hwdev.set_derivation_path(m_device_derivation_path);
+ hwdev.set_callback(get_device_callback());
m_account.create_from_device(hwdev);
m_key_device_type = m_account.get_device().get_type();
@@ -3973,6 +4130,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ m_original_keys_available = false;
setup_keys(password);
m_device_name = device_name;
@@ -4085,6 +4243,15 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
m_multisig_derivations = derivations;
}
}
+
+ 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_keys_available = true;
+ }
clear();
MINFO("Creating view key...");
@@ -4518,8 +4685,8 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee
//----------------------------------------------------------------------------------------------------
void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
{
- std::string keys_file, wallet_file;
- do_prepare_file_names(file_path, keys_file, wallet_file);
+ std::string keys_file, wallet_file, mms_file;
+ do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
boost::system::error_code ignore;
keys_file_exists = boost::filesystem::exists(keys_file, ignore);
@@ -4573,7 +4740,7 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
- do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
+ do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -4766,6 +4933,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
MERROR("Failed to save rings, will try again next time");
}
+
+ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
}
//----------------------------------------------------------------------------------------------------
void wallet2::trim_hashchain()
@@ -4871,6 +5040,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_file = m_wallet_file;
const std::string old_keys_file = m_keys_file;
const std::string old_address_file = m_wallet_file + ".address.txt";
+ const std::string old_mms_file = m_mms_file;
// save keys to the new file
// if we here, main wallet file is saved and we only need to save keys and address files
@@ -4900,6 +5070,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
if (!r) {
LOG_ERROR("error removing file: " << old_address_file);
}
+ // remove old message store file
+ if (boost::filesystem::exists(old_mms_file))
+ {
+ r = boost::filesystem::remove(old_mms_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_mms_file);
+ }
+ }
} else {
// save to new file
#ifdef WIN32
@@ -4925,6 +5103,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
}
+
+ if (m_message_store.get_active())
+ {
+ // While the "m_message_store" object of course always exist, a file for the message
+ // store should only exist if the MMS is really active
+ m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
+ }
+
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::balance(uint32_t index_major) const
@@ -5139,6 +5325,10 @@ void wallet2::rescan_blockchain(bool hard, bool refresh)
m_transfers.clear();
m_key_images.clear();
m_pub_keys.clear();
+ m_unconfirmed_txs.clear();
+ m_payments.clear();
+ m_confirmed_txs.clear();
+ m_unconfirmed_payments.clear();
m_scanned_pool_txs[0].clear();
m_scanned_pool_txs[1].clear();
@@ -9238,9 +9428,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
auto & hwdev = get_account().get_device();
- if (!hwdev.has_ki_cold_sync()){
- throw std::invalid_argument("Device does not support cold ki sync protocol");
- }
+ CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
@@ -9251,7 +9439,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
- return import_key_images(ski, 0, spent, unspent);
+ // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
+ uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
+ m_device_last_key_image_sync = time(NULL);
+
+ return import_res;
}
//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
@@ -11985,4 +12177,52 @@ uint64_t wallet2::get_segregation_fork_height() const
void wallet2::generate_genesis(cryptonote::block& b) const {
cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
}
+//----------------------------------------------------------------------------------------------------
+mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const
+{
+ mms::multisig_wallet_state state;
+ state.nettype = m_nettype;
+ state.multisig = multisig(&state.multisig_is_ready);
+ state.has_multisig_partial_key_images = has_multisig_partial_key_images();
+ state.multisig_rounds_passed = m_multisig_rounds_passed;
+ state.num_transfer_details = m_transfers.size();
+ if (state.multisig)
+ {
+ THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Monero address not available");
+ state.address = m_original_address;
+ state.view_secret_key = m_original_view_secret_key;
+ }
+ else
+ {
+ state.address = m_account.get_keys().m_account_address;
+ state.view_secret_key = m_account.get_keys().m_view_secret_key;
+ }
+ state.mms_file=m_mms_file;
+ return state;
+}
+//----------------------------------------------------------------------------------------------------
+wallet_device_callback * wallet2::get_device_callback()
+{
+ if (!m_device_callback){
+ m_device_callback.reset(new wallet_device_callback(this));
+ }
+ return m_device_callback.get();
+}//----------------------------------------------------------------------------------------------------
+void wallet2::on_button_request()
+{
+ if (0 != m_callback)
+ m_callback->on_button_request();
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::on_pin_request(epee::wipeable_string & pin)
+{
+ if (0 != m_callback)
+ m_callback->on_pin_request(pin);
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
+{
+ if (0 != m_callback)
+ m_callback->on_passphrase_request(on_device, passphrase);
+}
}