Compare commits

...

20 Commits

Author SHA1 Message Date
jw 56dea1b584
Merge pull request #121 from wowario/checkpoints
6 years ago
wowario 0ececcd9a1
version 0.3.1.5
6 years ago
wowario 6f9bb2b2ee
update checkpoints
6 years ago
wowario de5e3660d9
Merge pull request #119 from wowario/checkpoints
6 years ago
wowario 7bc2142590
correct cumulative difficulty count
6 years ago
wowario 8adea0a034
update checkpoints
6 years ago
wowario 58d006566b
Merge pull request #118 from wowario/checkpoints
6 years ago
wowario 3263067b97
update checkpoint
6 years ago
wowario 6cf896f8ab
update checkpoints
6 years ago
wowario caae62e2ce
update checkpoints
6 years ago
wowario 4865345889
Merge pull request #117 from wowario/checkpoints
6 years ago
wowario 8b56a44adc
counter attack
6 years ago
wowario d1623175b2
update checkpoints
6 years ago
jw 0edab6407f
Merge pull request #116 from wowario/checkpoints
6 years ago
jw 7008314cb0
Merge pull request #115 from wowario/version
6 years ago
jw 35d15f2626
Merge pull request #114 from rbrunner7/mms-for-wow2
6 years ago
wowario 47b543b591
update checkpoints
6 years ago
wowario 398a21019c
up version
6 years ago
rbrunner7 fc915d3e0f MMS: Some Wownero-specific changes in 'simplewallet.cpp'
6 years ago
rbrunner7 b1e005b3c8 MMS: Initial version for Wownero, merged in from Monero
6 years ago

@ -95,7 +95,7 @@ Dates are provided in the format YYYY-MM-DD.
| ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- | | ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- |
| 1 | 2018-04-01 | v7 | v0.1.0.0 | v0.1.0.0 | Cryptonight variant 1, ringsize >= 8, sorted inputs | 1 | 2018-04-01 | v7 | v0.1.0.0 | v0.1.0.0 | Cryptonight variant 1, ringsize >= 8, sorted inputs
| 6969 | 2018-04-24 | v8 | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4 | 6969 | 2018-04-24 | v8 | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4
| 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.1.0 | Cryptonight variant 2, LWMA v2, ringsize = 22, XXX | 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS
X's indicate that these details have not been determined as of commit date. X's indicate that these details have not been determined as of commit date.

Binary file not shown.

@ -191,6 +191,21 @@ namespace cryptonote
ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff"); ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff");
ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177"); ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177");
ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e"); ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e");
ADD_CHECKPOINT(59900, "18cc0653ef39cb304c68045dba5eb6b885f936281cd939dea04d0e6c9cd4ae2e");
ADD_CHECKPOINT(60000, "0f02aa57a63f79f63dafed9063abe228a37cb19f00430dc3168b8a8f4ae8016c");
ADD_CHECKPOINT(61000, "509aca8c54eb5fe44623768757b6e890ae39d512478c75f614cbff3d91809350");
ADD_CHECKPOINT(62000, "7fe91ad256c08dbd961e04738968be22fb481093fbfa7959bde7796ccceba0e2");
ADD_CHECKPOINT(62150, "1a7c75f8ebeda0e20eb5877181eafd7db0fc887e3fed43e0b27ab2e7bccafd10");
ADD_CHECKPOINT(62269, "4969555d60742afb93925fd96d83ac28f45e6e3c0e583c9fb3c92d9b2100d38f");
ADD_CHECKPOINT(62405, "4d0ae890cf9f875f231c7069508ad28dc429d14814b52db114dfab7519a27584");
ADD_CHECKPOINT(62419, "bd8bf5ac4c4fb07ab4d0d492bd1699def5c095ab6943ad3b63a89d1d8b1ce748");
ADD_CHECKPOINT(62425, "41a922dba6f3906871b2ccaf31ec9c91033470c503959093dae796deda8940ea");
ADD_CHECKPOINT(62479, "a2e8ff4205ba2980eb70921b0b21b5fc656ee273664ea94b860c68ca069b60dd");
ADD_CHECKPOINT(62503, "25fa115962988b4b8f8cfd22744a3e653b22ead8c8468e64caf334fc75a97d08");
ADD_CHECKPOINT(62550, "bde522a8a81c392c98c979434aa1dd9d20b4ca52230ba6ae0362872757808a48");
ADD_CHECKPOINT(62629, "8368e1ce1d421f1fc969364558433e2b2363d0ffcb5f2d946633095e3e6734f5");
ADD_CHECKPOINT(62720, "f871cddd75951e2fe24c282d2bd28396fc922ea519b354ace992a0162cb333ff");
ADD_CHECKPOINT(62733, "8331dbeeaf23173d2235a062373a437befadb6492cceb7640127bf18653a9e61");
return true; return true;
} }

@ -256,9 +256,9 @@ namespace cryptonote {
assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= static_cast<uint64_t>(N+1) ); assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= static_cast<uint64_t>(N+1) );
if ( height <= DIFFICULTY_HEIGHT ){ /*if ( height <= DIFFICULTY_HEIGHT ){
return static_cast<uint64_t>(DIFFICULTY_GUESS); return static_cast<uint64_t>(DIFFICULTY_GUESS);
} }*/
for ( int64_t i = 1; i <= N; i++ ) { for ( int64_t i = 1; i <= N; i++ ) {
ST = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i-1]); ST = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i-1]);

@ -4452,7 +4452,7 @@ void Blockchain::cancel()
} }
#if defined(PER_BLOCK_CHECKPOINT) #if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "897d3ae0f3aef8ef8362b0bb3535925738414fa78d9146f8a04c60555fa5a95f"; static const char expected_block_hashes_hash[] = "991bae8e227c5ba51767522aaea4fc7c1e54be8528793fe9301aff8f3031b7d7";
void Blockchain::load_compiled_in_block_hashes() void Blockchain::load_compiled_in_block_hashes()
{ {
const bool testnet = m_nettype == TESTNET; const bool testnet = m_nettype == TESTNET;

File diff suppressed because it is too large Load Diff

@ -210,6 +210,7 @@ namespace cryptonote
bool sign_multisig(const std::vector<std::string>& args); bool sign_multisig(const std::vector<std::string>& args);
bool submit_multisig(const std::vector<std::string>& args); bool submit_multisig(const std::vector<std::string>& args);
bool export_raw_multisig(const std::vector<std::string>& args); bool export_raw_multisig(const std::vector<std::string>& args);
bool mms(const std::vector<std::string>& args);
bool print_ring(const std::vector<std::string>& args); bool print_ring(const std::vector<std::string>& args);
bool set_ring(const std::vector<std::string>& args); bool set_ring(const std::vector<std::string>& args);
bool save_known_rings(const std::vector<std::string>& args); bool save_known_rings(const std::vector<std::string>& args);
@ -351,5 +352,38 @@ namespace cryptonote
bool m_auto_refresh_refreshing; bool m_auto_refresh_refreshing;
std::atomic<bool> m_in_manual_refresh; std::atomic<bool> m_in_manual_refresh;
uint32_t m_current_subaddress_account; uint32_t m_current_subaddress_account;
// MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };
mms::multisig_wallet_state get_multisig_wallet_state() const { return m_wallet->get_multisig_wallet_state(); };
bool mms_active() const { return get_message_store().get_active(); };
bool choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice);
void list_mms_messages(const std::vector<mms::message> &messages);
void show_message(const mms::message &m);
void ask_send_all_ready_messages();
void check_for_messages();
bool user_confirms(const std::string &question);
bool get_message_from_arg(const std::string &arg, mms::message &m);
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
void mms_init(const std::vector<std::string> &args);
void mms_info(const std::vector<std::string> &args);
void mms_member(const std::vector<std::string> &args);
void mms_list(const std::vector<std::string> &args);
void mms_next(const std::vector<std::string> &args);
void mms_sync(const std::vector<std::string> &args);
void mms_transfer(const std::vector<std::string> &args);
void mms_delete(const std::vector<std::string> &args);
void mms_send(const std::vector<std::string> &args);
void mms_receive(const std::vector<std::string> &args);
void mms_note(const std::vector<std::string> &args);
void mms_show(const std::vector<std::string> &args);
void mms_set(const std::vector<std::string> &args);
void mms_help(const std::vector<std::string> &args);
void mms_debug(const std::vector<std::string> &args);
bool m_called_by_mms = false;
bool called_by_mms();
bool m_command_successful;
}; };
} }

@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.3.1.0" #define DEF_MONERO_VERSION "0.3.1.5"
#define DEF_MONERO_RELEASE_NAME "Cool Cage" #define DEF_MONERO_RELEASE_NAME "Cool Cage"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG

@ -34,7 +34,10 @@ set(wallet_sources
wallet2.cpp wallet2.cpp
wallet_args.cpp wallet_args.cpp
ringdb.cpp ringdb.cpp
node_rpc_proxy.cpp) node_rpc_proxy.cpp
message_store.cpp
message_transporter.cpp
)
set(wallet_private_headers set(wallet_private_headers
wallet2.h wallet2.h
@ -44,7 +47,9 @@ set(wallet_private_headers
wallet_rpc_server_commands_defs.h wallet_rpc_server_commands_defs.h
wallet_rpc_server_error_codes.h wallet_rpc_server_error_codes.h
ringdb.h ringdb.h
node_rpc_proxy.h) node_rpc_proxy.h
message_store.h
message_transporter.h)
monero_private_headers(wallet monero_private_headers(wallet
${wallet_private_headers}) ${wallet_private_headers})
@ -102,7 +107,6 @@ set_property(TARGET wallet_rpc_server
OUTPUT_NAME "wownero-wallet-rpc") OUTPUT_NAME "wownero-wallet-rpc")
install(TARGETS wallet_rpc_server DESTINATION bin) install(TARGETS wallet_rpc_server DESTINATION bin)
# build and install libwallet_merged only if we building for GUI # build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS) if (BUILD_GUI_DEPS)
set(libs_to_merge set(libs_to_merge

File diff suppressed because it is too large Load Diff

@ -0,0 +1,357 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <cstdlib>
#include <string>
#include <vector>
#include "crypto/hash.h"
#include <boost/serialization/vector.hpp>
#include "serialization/serialization.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "common/i18n.h"
#include "message_transporter.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
namespace mms
{
enum class message_type
{
key_set,
finalizing_key_set,
multisig_sync_data,
partially_signed_tx,
fully_signed_tx,
note
};
enum class message_direction
{
in,
out
};
enum class message_state
{
ready_to_send,
sent,
waiting,
processed,
cancelled
};
enum class message_processing
{
prepare_multisig,
make_multisig,
finalize_multisig,
create_sync_data,
process_sync_data,
sign_tx,
send_tx,
submit_tx
};
struct message
{
uint32_t id;
message_type type;
message_direction direction;
std::string content;
uint64_t created;
uint64_t modified;
uint64_t sent;
uint32_t member_index;
crypto::hash hash;
message_state state;
uint32_t wallet_height;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
};
// "wallet_height" (for lack of a short name that would describe what it is about)
// is the number of transfers present in the wallet at the time of message
// construction; used to coordinate generation of sync info (which depends
// on the content of the wallet at time of generation)
struct coalition_member
{
std::string label;
std::string transport_address;
bool monero_address_known;
cryptonote::account_public_address monero_address;
bool me;
uint32_t index;
};
struct processing_data
{
message_processing processing;
std::vector<uint32_t> message_ids;
uint32_t receiving_member_index = 0;
};
struct file_transport_message
{
cryptonote::account_public_address sender_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
message internal_message;
};
// Overal .mms file structure, with the "message_store" object serialized to and
// encrypted in "encrypted_data"
struct file_data
{
std::string magic_string;
uint32_t file_version;
crypto::chacha_iv iv;
std::string encrypted_data;
};
// The following struct provides info about the current state of a "wallet2" object
// at the time of a "message_store" method call that those methods need. See on the
// one hand a first parameter of this type for several of those methods, and on the
// other hand the method "wallet2::get_multisig_wallet_state" which clients like the
// CLI wallet can use to get that info.
//
// Note that in the case of a wallet that is already multisig "address" is NOT the
// multisig address, but the "original" wallet address at creation time. Likewise
// "view_secret_key" is the original view secret key then.
//
// This struct definition is here and not in "wallet2.h" to avoid circular imports.
struct multisig_wallet_state
{
cryptonote::account_public_address address;
cryptonote::network_type nettype;
crypto::secret_key view_secret_key;
bool multisig;
bool multisig_is_ready;
bool has_multisig_partial_key_images;
size_t num_transfer_details;
std::string mms_file;
~multisig_wallet_state()
{
view_secret_key = crypto::null_skey;
}
};
class message_store
{
public:
message_store();
// Initialize and start to use the MMS, set the first member, this wallet itself
// Filename, if not null and not empty, is used to create the ".mms" file
// reset it if already used, with deletion of all members and messages
void init(const multisig_wallet_state &state, const std::string &own_label,
const std::string &own_transport_address, uint32_t coalition_size, uint32_t threshold);
void set_active(bool active) { m_active = active; };
void set_auto_send(bool auto_send) { m_auto_send = auto_send; };
void set_options(const boost::program_options::variables_map& vm);
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool get_active() const { return m_active; };
bool get_auto_send() const { return m_auto_send; };
uint32_t get_threshold() const { return m_threshold; };
uint32_t get_coalition_size() const { return m_coalition_size; };
void set_member(const multisig_wallet_state &state,
uint32_t index,
const boost::optional<std::string> &label,
const boost::optional<std::string> &transport_address,
const boost::optional<cryptonote::account_public_address> monero_address);
const coalition_member &get_member(uint32_t index) const;
bool get_member_index_by_monero_address(const cryptonote::account_public_address &monero_address, uint32_t &index) const;
bool get_member_index_by_label(const std::string label, uint32_t &index) const;
const std::vector<coalition_member> &get_all_members() const { return m_members; };
bool member_info_complete() const;
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
// Creates the resulting messages to the right members
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content);
// Go through all the messages, look at the "ready to process" ones, and check whether any single one
// or any group of them can be processed, because they are processable as single messages (like a tx
// that is fully signed and thus ready for submit to the net) or because they form a complete group
// (e.g. key sets from all coalition members to make the wallet multisig). If there are multiple
// candidates, e.g. in 2/3 multisig sending to one OR the other member to sign, there will be more
// than 1 element in 'data' for the user to choose. If nothing is ready "false" is returned.
// The method mostly ignores the order in which the messages were received because messages may be delayed
// (e.g. sync data from a member arrives AFTER a transaction to submit) or because message time stamps
// may be wrong so it's not possible to order them reliably.
// Messages also may be ready by themselves but the wallet not yet ready for them (e.g. sync data already
// arriving when the wallet is not yet multisig because key sets were delayed or were lost altogether.)
// If nothing is ready 'wait_reason' may contain further info about the reason why.
bool get_processable_messages(const multisig_wallet_state &state,
bool force_sync,
std::vector<processing_data> &data_list,
std::string &wait_reason);
void set_messages_processed(const processing_data &data);
uint32_t add_message(const multisig_wallet_state &state,
uint32_t member_index, message_type type, message_direction direction,
const std::string &content);
const std::vector<message> &get_all_messages() const { return m_messages; };
bool get_message_by_id(uint32_t id, message &m) const;
message get_message_by_id(uint32_t id) const;
void set_message_processed_or_sent(uint32_t id);
void delete_message(uint32_t id);
void delete_all_messages();
void send_message(const multisig_wallet_state &state, uint32_t id);
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);
void stop() { m_run.store(false, std::memory_order_relaxed); m_transporter.stop(); }
void write_to_file(const multisig_wallet_state &state, const std::string &filename);
void read_from_file(const multisig_wallet_state &state, const std::string &filename);
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
a & m_active;
a & m_coalition_size;
a & m_nettype;
a & m_threshold;
a & m_members;
a & m_messages;
a & m_next_message_id;
a & m_auto_send;
}
const char* message_type_to_string(message_type type);
const char* message_direction_to_string(message_direction direction);
const char* message_state_to_string(message_state state);
std::string member_to_string(const coalition_member &member, uint32_t max_width);
static const char *tr(const char *str) { return i18n_translate(str, "tools::mms"); }
static void init_options(boost::program_options::options_description& desc_params);
private:
bool m_active;
uint32_t m_coalition_size;
uint32_t m_threshold;
bool m_auto_send;
cryptonote::network_type m_nettype;
std::vector<coalition_member> m_members;
std::vector<message> m_messages;
uint32_t m_next_message_id;
std::string m_filename;
message_transporter m_transporter;
std::atomic<bool> m_run;
bool get_message_index_by_id(uint32_t id, uint32_t &index) const;
uint32_t get_message_index_by_id(uint32_t id) const;
bool any_message_of_type(message_type type, message_direction direction) const;
bool any_message_with_hash(const crypto::hash &hash) const;
bool message_ids_complete(const std::vector<uint32_t> ids) const;
void encrypt(uint32_t member_index, const std::string &plaintext,
std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv);
void decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv,
const crypto::secret_key &view_secret_key, std::string &plaintext);
void delete_transport_message(uint32_t id);
std::string account_address_to_string(const cryptonote::account_public_address &account_address) const;
void save(const multisig_wallet_state &state);
};
}
BOOST_CLASS_VERSION(mms::file_data, 0)
BOOST_CLASS_VERSION(mms::message_store, 0)
BOOST_CLASS_VERSION(mms::message, 0)
BOOST_CLASS_VERSION(mms::file_transport_message, 0)
BOOST_CLASS_VERSION(mms::coalition_member, 0)
namespace boost
{
namespace serialization
{
template <class Archive>
inline void serialize(Archive &a, mms::file_data &x, const boost::serialization::version_type ver)
{
a & x.magic_string;
a & x.file_version;
a & x.iv;
a & x.encrypted_data;
}
template <class Archive>
inline void serialize(Archive &a, mms::message &x, const boost::serialization::version_type ver)
{
a & x.id;
a & x.type;
a & x.direction;
a & x.content;
a & x.created;
a & x.modified;
a & x.sent;
a & x.member_index;
a & x.hash;
a & x.state;
a & x.wallet_height;
a & x.round;
a & x.signature_count;
a & x.transport_id;
}
template <class Archive>
inline void serialize(Archive &a, mms::coalition_member &x, const boost::serialization::version_type ver)
{
a & x.label;
a & x.transport_address;
a & x.monero_address_known;
a & x.monero_address;
a & x.me;
a & x.index;
}
template <class Archive>
inline void serialize(Archive &a, mms::file_transport_message &x, const boost::serialization::version_type ver)
{
a & x.sender_address;
a & x.iv;
a & x.encryption_public_key;
a & x.internal_message;
}
template <class Archive>
inline void serialize(Archive &a, crypto::chacha_iv &x, const boost::serialization::version_type ver)
{
a & x.data;
}
}
}

@ -0,0 +1,287 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "message_transporter.h"
#include "string_coding.h"
#include <boost/format.hpp>
#include "wallet_errors.h"
#include "net/http_client.h"
#include "net/net_parse_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
#define PYBITMESSAGE_DEFAULT_API_PORT 8442
namespace mms
{
namespace bitmessage_rpc
{
struct message_info
{
uint32_t encodingType;
std::string toAddress;
uint32_t read;
std::string msgid;
std::string message;
std::string fromAddress;
std::string receivedTime;
std::string subject;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(encodingType)
KV_SERIALIZE(toAddress)
KV_SERIALIZE(read)
KV_SERIALIZE(msgid)
KV_SERIALIZE(message);
KV_SERIALIZE(fromAddress)
KV_SERIALIZE(receivedTime)
KV_SERIALIZE(subject)
END_KV_SERIALIZE_MAP()
};
struct inbox_messages_response
{
std::vector<message_info> inboxMessages;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(inboxMessages)
END_KV_SERIALIZE_MAP()
};
}
message_transporter::message_transporter()
{
m_run = true;
}
void message_transporter::set_options(const std::string &bitmessage_address, const std::string &bitmessage_login) {
m_bitmessage_url = bitmessage_address;
epee::net_utils::http::url_content address_parts{};
epee::net_utils::parse_url(m_bitmessage_url, address_parts);
if (address_parts.port == 0)
{
address_parts.port = PYBITMESSAGE_DEFAULT_API_PORT;
}
auto pos = bitmessage_login.find(":");
if (pos == std::string::npos)
{
m_bitmessage_user = bitmessage_login;
m_bitmessage_password.clear();
}
else
{
m_bitmessage_user = bitmessage_login.substr(0, pos);
m_bitmessage_password = bitmessage_login.substr(pos + 1);
}
boost::optional<epee::net_utils::http::login> login{};
login.emplace(m_bitmessage_user, m_bitmessage_password);
m_http_client.set_server(address_parts.host, std::to_string(address_parts.port), login);
}
bool message_transporter::receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages)
{
// The message body of the Bitmessage message is basically the transport message, as JSON (and nothing more).
// Weeding out other, non-MMS messages is done in a simple way: If it deserializes without error, it's an MMS message
// That JSON is Base64-encoded by the MMS because the Monero epee JSON serializer does not escape anything and happily
// includes even 0 (NUL) in strings, which might confuse Bitmessage or at least display confusingly in the client.
// There is yet another Base64-encoding of course as part of the Bitmessage API for the message body parameter
// The Bitmessage API call "getAllInboxMessages" gives back a JSON array with all the messages (despite using
// XML-RPC for the calls, and not JSON-RPC ...)
m_run.store(true, std::memory_order_relaxed);
std::string request;
start_xml_rpc_cmd(request, "getAllInboxMessages");
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
std::string json = get_str_between_tags(answer, "<string>", "</string>");
bitmessage_rpc::inbox_messages_response bitmessage_res;
epee::serialization::load_t_from_json(bitmessage_res, json);
size_t size = bitmessage_res.inboxMessages.size();
messages.clear();
for (size_t i = 0; i < size; ++i)
{
if (!m_run.load(std::memory_order_relaxed))
{
// Stop was called, don't waste time processing any more messages
return false;
}
bitmessage_rpc::message_info message_info = bitmessage_res.inboxMessages[i];
transport_message message;
bool is_mms_message = false;
try
{
// First Base64-decoding: The message body is Base64 in the Bitmessage API
std::string message_body = epee::string_encoding::base64_decode(message_info.message);
// Second Base64-decoding: The MMS uses Base64 to hide non-textual data in its JSON from Bitmessage
json = epee::string_encoding::base64_decode(message_body);
epee::serialization::load_t_from_json(message, json);
is_mms_message = true;
}
catch(const std::exception& e)
{
}
if (is_mms_message)
{
if (message.destination_monero_address == destination_monero_address)
{
message.transport_id = message_info.msgid;
messages.push_back(message);
}
}
}
return true;
}
bool message_transporter::send_message(const transport_message &message)
{
// <toAddress> <fromAddress> <subject> <message> [encodingType [TTL]]
std::string request;
start_xml_rpc_cmd(request, "sendMessage");
add_xml_rpc_string_param(request, message.destination_transport_address);
add_xml_rpc_string_param(request, message.source_transport_address);
add_xml_rpc_base64_param(request, message.subject);
std::string json = epee::serialization::store_t_to_json(message);
std::string message_body = epee::string_encoding::base64_encode(json); // See comment in "receive_message" about reason for (double-)Base64 encoding
add_xml_rpc_base64_param(request, message_body);
add_xml_rpc_integer_param(request, 2);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::delete_message(const std::string &transport_id)
{
std::string request;
start_xml_rpc_cmd(request, "trashMessage");
add_xml_rpc_string_param(request, transport_id);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::post_request(const std::string &request, std::string &answer)
{
// Somehow things do not work out if one tries to connect "m_http_client" to Bitmessage
// and keep it connected over the course of several calls. But with a new connection per
// call and disconnecting after the call there is no problem (despite perhaps a small
// slowdown)
epee::net_utils::http::fields_list additional_params;
// Basic access authentication according to RFC 7617 (which the epee HTTP classes do not seem to support?)
std::string user_password(m_bitmessage_user + ":" + m_bitmessage_password);
std::string auth_string = epee::string_encoding::base64_encode(user_password);
auth_string.insert(0, "Basic ");
additional_params.push_back(std::make_pair("Authorization", auth_string));
additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8"));
const epee::net_utils::http::http_response_info* response = NULL;
std::chrono::milliseconds timeout = std::chrono::seconds(15);
bool r = m_http_client.invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params));
if (r)
{
answer = response->m_body;
}
else
{
LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300));
THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url);
}
m_http_client.disconnect(); // see comment above
std::string string_value = get_str_between_tags(answer, "<string>", "</string>");
if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0))
{
THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value);
}
return r;
}
// Pick some string between two delimiters
// When parsing the XML returned by PyBitmessage, don't bother to fully parse it but as a little hack rely on the
// fact that e.g. a single string returned will be, however deeply nested in "<params><param><value>...", delivered
// between the very first "<string>" and "</string>" tags to be found in the XML
std::string message_transporter::get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim)
{
size_t first_delim_pos = s.find(start_delim);
if (first_delim_pos != std::string::npos)
{
size_t end_pos_of_first_delim = first_delim_pos + start_delim.length();
size_t last_delim_pos = s.find(stop_delim);
if (last_delim_pos != std::string::npos)
{
return s.substr(end_pos_of_first_delim, last_delim_pos - end_pos_of_first_delim);
}
}
return std::string();
}
void message_transporter::start_xml_rpc_cmd(std::string &xml, const std::string &method_name)
{
xml = (boost::format("<?xml version=\"1.0\"?><methodCall><methodName>%s</methodName><params>") % method_name).str();
}
void message_transporter::add_xml_rpc_string_param(std::string &xml, const std::string &param)
{
xml += (boost::format("<param><value><string>%s</string></value></param>") % param).str();
}
void message_transporter::add_xml_rpc_base64_param(std::string &xml, const std::string &param)
{
// Bitmessage expects some arguments Base64-encoded, but it wants them as parameters of type "string", not "base64" that is also part of XML-RPC
std::string encoded_param = epee::string_encoding::base64_encode(param);
xml += (boost::format("<param><value><string>%s</string></value></param>") % encoded_param).str();
}
void message_transporter::add_xml_rpc_integer_param(std::string &xml, const int32_t &param)
{
xml += (boost::format("<param><value><int>%i</int></value></param>") % param).str();
}
void message_transporter::end_xml_rpc_cmd(std::string &xml)
{
xml += "</params></methodCall>";
}
}

@ -0,0 +1,113 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "serialization/keyvalue_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "net/http_server_impl_base.h"
#include "net/http_client.h"
#include "common/util.h"
#include "serialization/keyvalue_serialization.h"
#define PYBITMESSAGE_API_PORT 8442
namespace mms
{
struct transport_message
{
cryptonote::account_public_address source_monero_address;
std::string source_transport_address;
cryptonote::account_public_address destination_monero_address;
std::string destination_transport_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
uint64_t timestamp;
uint32_t type;
std::string subject;
std::string content;
crypto::hash hash;
crypto::signature signature;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(source_monero_address)
KV_SERIALIZE(source_transport_address)
KV_SERIALIZE(destination_monero_address)
KV_SERIALIZE(destination_transport_address)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(iv)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(encryption_public_key)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(type)
KV_SERIALIZE(subject)
KV_SERIALIZE(content)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(hash)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(signature)
KV_SERIALIZE(round)
KV_SERIALIZE(signature_count)
KV_SERIALIZE(transport_id)
END_KV_SERIALIZE_MAP()
};
class message_transporter {
public:
message_transporter();
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool send_message(const transport_message &message);
bool receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages);
bool delete_message(const std::string &transport_id);
void stop() { m_run.store(false, std::memory_order_relaxed); }
private:
epee::net_utils::http::http_simple_client m_http_client;
std::string m_bitmessage_url;
std::string m_bitmessage_user;
std::string m_bitmessage_password;
std::atomic<bool> m_run;
bool post_request(const std::string &request, std::string &answer);
std::string get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim);
void start_xml_rpc_cmd(std::string &xml, const std::string &method_name);
void add_xml_rpc_string_param(std::string &xml, const std::string &param);
void add_xml_rpc_base64_param(std::string &xml, const std::string &param);
void add_xml_rpc_integer_param(std::string &xml, const int32_t &param);
void end_xml_rpc_cmd(std::string &xml);
};
}

@ -153,7 +153,7 @@ struct options {
}; };
}; };
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; keys_file = file_path;
wallet_file = file_path; wallet_file = file_path;
@ -165,6 +165,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
{//provided wallet file name {//provided wallet file name
keys_file += ".keys"; keys_file += ".keys";
} }
mms_file = file_path + ".mms";
} }
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier) uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
@ -230,6 +231,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(std::move(daemon_address), std::move(login)); wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string()); wallet->set_ring_database(ringdb_path.string());
wallet->get_message_store().set_options(vm);
return wallet; return wallet;
} }
@ -676,6 +678,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_connected(false), m_light_wallet_connected(false),
m_light_wallet_balance(0), m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0), m_light_wallet_unlocked_balance(0),
m_original_keys_available(false),
m_message_store(),
m_key_on_device(false), m_key_on_device(false),
m_ring_history_saved(false), m_ring_history_saved(false),
m_ringdb() m_ringdb()
@ -709,6 +713,7 @@ 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.stagenet);
command_line::add_arg(desc_params, opts.restricted); command_line::add_arg(desc_params, opts.restricted);
command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.shared_ringdb_dir);
mms::message_store::init_options(desc_params);
} }
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@ -2581,6 +2586,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
rapidjson::Document json; rapidjson::Document json;
json.SetObject(); json.SetObject();
rapidjson::Value value(rapidjson::kStringType); rapidjson::Value value(rapidjson::kStringType);
value.SetString(account_data.c_str(), account_data.length()); value.SetString(account_data.c_str(), account_data.length());
json.AddMember("key_data", value, json.GetAllocator()); json.AddMember("key_data", value, json.GetAllocator());
if (!seed_language.empty()) if (!seed_language.empty())
@ -2683,6 +2689,22 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_subaddress_lookahead_minor); value2.SetUint(m_subaddress_lookahead_minor);
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator()); json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
value2.SetInt(m_original_keys_available ? 1 : 0);
json.AddMember("original_keys_available", value2, 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 // Serialize the JSON object
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@ -2758,6 +2780,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = 0; m_segregation_height = 0;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_original_keys_available = false;
m_key_on_device = false; m_key_on_device = false;
} }
else if(json.IsObject()) else if(json.IsObject())
@ -2884,6 +2907,44 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_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); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor; m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
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, false, std::string());
if (!field_original_address_found)
{
LOG_ERROR("Field original_address not found in JSON");
return false;
}
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, false, std::string());
if (!field_original_view_secret_key_found)
{
LOG_ERROR("Field original_view_secret_key not found in JSON");
return false;
}
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");
}
}
}
else
{
m_original_keys_available = false;
}
} }
else else
{ {
@ -3051,6 +3112,10 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_signers = multisig_signers; m_multisig_signers = multisig_signers;
m_key_on_device = false; m_key_on_device = false;
// 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;
if (!wallet_.empty()) if (!wallet_.empty())
{ {
bool r = store_keys(m_keys_file, password, false); bool r = store_keys(m_keys_file, password, false);
@ -3101,6 +3166,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig = false; m_multisig = false;
m_multisig_threshold = 0; m_multisig_threshold = 0;
m_multisig_signers.clear(); m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false; m_key_on_device = false;
// calculate a starting refresh height // calculate a starting refresh height
@ -3199,6 +3265,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false; m_multisig = false;
m_multisig_threshold = 0; m_multisig_threshold = 0;
m_multisig_signers.clear(); m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false; m_key_on_device = false;
if (!wallet_.empty()) if (!wallet_.empty())
@ -3249,6 +3316,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false; m_multisig = false;
m_multisig_threshold = 0; m_multisig_threshold = 0;
m_multisig_signers.clear(); m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false; m_key_on_device = false;
if (!wallet_.empty()) if (!wallet_.empty())
@ -3295,6 +3363,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig = false; m_multisig = false;
m_multisig_threshold = 0; m_multisig_threshold = 0;
m_multisig_signers.clear(); m_multisig_signers.clear();
m_original_keys_available = false;
if (!wallet_.empty()) { if (!wallet_.empty()) {
bool r = store_keys(m_keys_file, password, false); bool r = store_keys(m_keys_file, password, false);
@ -3369,6 +3438,15 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case"); CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case");
} }
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;
}
// the multisig view key is shared by all, make one all can derive // the multisig view key is shared by all, make one all can derive
MINFO("Creating view key..."); 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); crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
@ -3696,8 +3774,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) void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
{ {
std::string keys_file, wallet_file; std::string keys_file, wallet_file, mms_file;
do_prepare_file_names(file_path, keys_file, wallet_file); do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
boost::system::error_code ignore; boost::system::error_code ignore;
keys_file_exists = boost::filesystem::exists(keys_file, ignore); keys_file_exists = boost::filesystem::exists(keys_file, ignore);
@ -3751,7 +3829,7 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path) 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; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -3922,6 +4000,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{ {
MERROR("Failed to save rings, will try again next time"); 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() void wallet2::trim_hashchain()
@ -4029,6 +4109,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_file = m_wallet_file;
const std::string old_keys_file = m_keys_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_address_file = m_wallet_file + ".address.txt";
const std::string old_mms_file = m_mms_file;
// save keys to the new 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 // if we here, main wallet file is saved and we only need to save keys and address files
@ -4058,6 +4139,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
if (!r) { if (!r) {
LOG_ERROR("error removing file: " << old_address_file); 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 { } else {
// save to new file // save to new file
#ifdef WIN32 #ifdef WIN32
@ -4083,6 +4172,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); 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); 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 uint64_t wallet2::balance(uint32_t index_major) const
@ -10561,4 +10658,28 @@ void wallet2::generate_genesis(cryptonote::block& b) const {
cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE); cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
} }
} }
//----------------------------------------------------------------------------------------------------
mms::multisig_wallet_state wallet2::get_multisig_wallet_state()
{
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.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;
}
} }

@ -58,6 +58,7 @@
#include "wallet_errors.h" #include "wallet_errors.h"
#include "common/password.h" #include "common/password.h"
#include "node_rpc_proxy.h" #include "node_rpc_proxy.h"
#include "message_store.h"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@ -454,6 +455,7 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry; typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*! /*!
* \brief Generates a wallet or restores one. * \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file * \param wallet_ Name of wallet file
@ -593,7 +595,7 @@ namespace tools
bool init(std::string daemon_address = "http://localhost:8080", bool init(std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false); boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
void stop() { m_run.store(false, std::memory_order_relaxed); } void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
i_wallet2_callback* callback() const { return m_callback; } i_wallet2_callback* callback() const { return m_callback; }
void callback(i_wallet2_callback* callback) { m_callback = callback; } void callback(i_wallet2_callback* callback) { m_callback = callback; }
@ -1077,6 +1079,10 @@ namespace tools
bool unblackball_output(const crypto::public_key &output); bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const; bool is_output_blackballed(const crypto::public_key &output) const;
// MMS -------------------------------------------------------------------------------------------------
mms::message_store& get_message_store() { return m_message_store; };
mms::multisig_wallet_state get_multisig_wallet_state();
private: private:
/*! /*!
* \brief Stores wallet information to wallet file. * \brief Stores wallet information to wallet file.
@ -1148,6 +1154,7 @@ namespace tools
std::string m_daemon_address; std::string m_daemon_address;
std::string m_wallet_file; std::string m_wallet_file;
std::string m_keys_file; std::string m_keys_file;
std::string m_mms_file;
epee::net_utils::http::http_simple_client m_http_client; epee::net_utils::http::http_simple_client m_http_client;
hashchain m_blockchain; hashchain m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::atomic<uint64_t> m_local_bc_height; //temporary workaround
@ -1232,6 +1239,11 @@ namespace tools
std::string m_ring_database; std::string m_ring_database;
bool m_ring_history_saved; bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb; std::unique_ptr<ringdb> m_ringdb;
mms::message_store m_message_store;
bool m_original_keys_available;
cryptonote::account_public_address m_original_address;
crypto::secret_key m_original_view_secret_key;
}; };
} }
BOOST_CLASS_VERSION(tools::wallet2, 24) BOOST_CLASS_VERSION(tools::wallet2, 24)

@ -784,6 +784,31 @@ namespace tools
std::string m_wallet_file; std::string m_wallet_file;
}; };
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct mms_error : public wallet_logic_error
{
protected:
explicit mms_error(std::string&& loc, const std::string& message)
: wallet_logic_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
struct no_connection_to_bitmessage : public mms_error
{
explicit no_connection_to_bitmessage(std::string&& loc, const std::string& address)
: mms_error(std::move(loc), "no connection to PyBitmessage at address " + address)
{
}
};
//----------------------------------------------------------------------------------------------------
struct bitmessage_api_error : public mms_error
{
explicit bitmessage_api_error(std::string&& loc, const std::string& error_string)
: mms_error(std::move(loc), "PyBitmessage returned " + error_string)
{
}
};
//----------------------------------------------------------------------------------------------------
#if !defined(_MSC_VER) #if !defined(_MSC_VER)

Loading…
Cancel
Save