MMS: Initial version for Wownero, merged in from Monero
Wownero-specific changes still missing, see following second commit
This commit is contained in:
parent
d4a5778dd6
commit
b1e005b3c8
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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ¶m)
|
||||||
|
{
|
||||||
|
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 ¶m)
|
||||||
|
{
|
||||||
|
// 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 ¶m)
|
||||||
|
{
|
||||||
|
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 ¶m);
|
||||||
|
void add_xml_rpc_base64_param(std::string &xml, const std::string ¶m);
|
||||||
|
void add_xml_rpc_integer_param(std::string &xml, const int32_t ¶m);
|
||||||
|
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…
Reference in New Issue