mirror of https://github.com/oxen-io/oxen-core.git
1047 lines
31 KiB
C++
1047 lines
31 KiB
C++
// (Included from simplewallet.cpp when compiling with MMS support)
|
|
|
|
// MMS ---------------------------------------------------------------------------------------------------
|
|
|
|
// Access to the message store, or more exactly to the list of the messages that can be changed
|
|
// by the idle thread, is guarded by the same mutex-based mechanism as access to the wallet
|
|
// as a whole and thus e.g. uses the "LOCK_IDLE_SCOPE" macro. This is a little over-cautious, but
|
|
// simple and safe. Care has to be taken however where MMS methods call other simplewallet methods
|
|
// that use "LOCK_IDLE_SCOPE" as this cannot be nested!
|
|
|
|
// Methods for commands like "export_multisig_info" usually read data from file(s) or write data
|
|
// to files. The MMS calls now those methods as well, to produce data for messages and to process data
|
|
// from messages. As it would be quite inconvenient for the MMS to write data for such methods to files
|
|
// first or get data out of result files after the call, those methods detect a call from the MMS and
|
|
// expect data as arguments instead of files and give back data by calling 'process_wallet_created_data'.
|
|
|
|
//----------------------------------------------------------------------------------------------------
|
|
void simple_wallet::check_for_messages()
|
|
{
|
|
try
|
|
{
|
|
std::vector<mms::message> new_messages;
|
|
bool new_message = get_message_store().check_for_messages(get_multisig_wallet_state(), new_messages);
|
|
if (new_message)
|
|
{
|
|
message_writer(epee::console_color_magenta, true) << tr("MMS received new message");
|
|
list_mms_messages(new_messages);
|
|
m_cmd_binder.print_prompt();
|
|
}
|
|
}
|
|
catch(...) {}
|
|
}
|
|
//----------------------------------------------------------------------------------------------------
|
|
bool simple_wallet::check_mms()
|
|
{
|
|
// Check for new MMS messages;
|
|
// For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no
|
|
// separate thread either; thread syncing is tricky enough with only this one idle thread here
|
|
if (m_auto_refresh_enabled && get_message_store().get_active())
|
|
{
|
|
check_for_messages();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool simple_wallet::user_confirms(const std::string &question)
|
|
{
|
|
std::string answer = input_line(question + tr(" (Y/Yes/N/No): "));
|
|
return !std::cin.eof() && command_line::is_yes(answer);
|
|
}
|
|
|
|
bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
|
|
{
|
|
bool valid = false;
|
|
try
|
|
{
|
|
number = boost::lexical_cast<uint32_t>(arg);
|
|
valid = (number >= lower_bound) && (number <= upper_bound);
|
|
}
|
|
catch(const boost::bad_lexical_cast &)
|
|
{
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
bool simple_wallet::choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice)
|
|
{
|
|
size_t choices = data_list.size();
|
|
if (choices == 1)
|
|
{
|
|
choice = 0;
|
|
return true;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
message_writer() << tr("Choose processing:");
|
|
std::string text;
|
|
for (size_t i = 0; i < choices; ++i)
|
|
{
|
|
const mms::processing_data &data = data_list[i];
|
|
text = std::to_string(i+1) + ": ";
|
|
switch (data.processing)
|
|
{
|
|
case mms::message_processing::sign_tx:
|
|
text += tr("Sign tx");
|
|
break;
|
|
case mms::message_processing::send_tx:
|
|
{
|
|
mms::message m;
|
|
ms.get_message_by_id(data.message_ids[0], m);
|
|
if (m.type == mms::message_type::fully_signed_tx)
|
|
{
|
|
text += tr("Send the tx for submission to ");
|
|
}
|
|
else
|
|
{
|
|
text += tr("Send the tx for signing to ");
|
|
}
|
|
mms::authorized_signer signer = ms.get_signer(data.receiving_signer_index);
|
|
text += ms.signer_to_string(signer, 50);
|
|
break;
|
|
}
|
|
case mms::message_processing::submit_tx:
|
|
text += tr("Submit tx");
|
|
break;
|
|
default:
|
|
text += tr("unknown");
|
|
break;
|
|
}
|
|
message_writer() << text;
|
|
}
|
|
|
|
std::string line = input_line(tr("Choice: "));
|
|
if (std::cin.eof() || line.empty())
|
|
{
|
|
return false;
|
|
}
|
|
bool choice_ok = get_number_from_arg(line, choice, 1, choices);
|
|
if (choice_ok)
|
|
{
|
|
choice--;
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Wrong choice");
|
|
}
|
|
return choice_ok;
|
|
}
|
|
|
|
void simple_wallet::list_mms_messages(const std::vector<mms::message> &messages)
|
|
{
|
|
message_writer() << boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") % tr("Id") % tr("I/O") % tr("Authorized Signer")
|
|
% tr("Message Type") % tr("Height") % tr("R") % tr("Message State") % tr("Since");
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
uint64_t now = (uint64_t)time(NULL);
|
|
for (size_t i = 0; i < messages.size(); ++i)
|
|
{
|
|
const mms::message &m = messages[i];
|
|
const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
|
|
bool highlight = (m.state == mms::message_state::ready_to_send) || (m.state == mms::message_state::waiting);
|
|
message_writer(m.direction == mms::message_direction::out ? epee::console_color_green : epee::console_color_magenta, highlight) <<
|
|
boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
|
|
m.id %
|
|
ms.message_direction_to_string(m.direction) %
|
|
ms.signer_to_string(signer, 30) %
|
|
ms.message_type_to_string(m.type) %
|
|
m.wallet_height %
|
|
m.round %
|
|
ms.message_state_to_string(m.state) %
|
|
(tools::get_human_readable_timestamp(m.modified) + ", " + tools::get_human_readable_timespan(std::chrono::seconds(now - m.modified)) + tr(" ago"));
|
|
}
|
|
}
|
|
|
|
void simple_wallet::list_signers(const std::vector<mms::authorized_signer> &signers)
|
|
{
|
|
message_writer() << boost::format("%2s %-20s %-s") % tr("#") % tr("Label") % tr("Transport Address");
|
|
message_writer() << boost::format("%2s %-20s %-s") % "" % tr("Auto-Config Token") % tr("Oxen Address");
|
|
for (size_t i = 0; i < signers.size(); ++i)
|
|
{
|
|
const mms::authorized_signer &signer = signers[i];
|
|
std::string label = signer.label.empty() ? tr("<not set>") : signer.label;
|
|
std::string monero_address;
|
|
if (signer.monero_address_known)
|
|
{
|
|
monero_address = get_account_address_as_str(m_wallet->nettype(), false, signer.monero_address);
|
|
}
|
|
else
|
|
{
|
|
monero_address = tr("<not set>");
|
|
}
|
|
std::string transport_address = signer.transport_address.empty() ? tr("<not set>") : signer.transport_address;
|
|
message_writer() << boost::format("%2s %-20s %-s") % (i + 1) % label % transport_address;
|
|
message_writer() << boost::format("%2s %-20s %-s") % "" % signer.auto_config_token % monero_address;
|
|
message_writer() << "";
|
|
}
|
|
}
|
|
|
|
void simple_wallet::add_signer_config_messages()
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
std::string signer_config;
|
|
ms.get_signer_config(signer_config);
|
|
|
|
const std::vector<mms::authorized_signer> signers = ms.get_all_signers();
|
|
mms::multisig_wallet_state state = get_multisig_wallet_state();
|
|
uint32_t num_authorized_signers = ms.get_num_authorized_signers();
|
|
for (uint32_t i = 1 /* without me */; i < num_authorized_signers; ++i)
|
|
{
|
|
ms.add_message(state, i, mms::message_type::signer_config, mms::message_direction::out, signer_config);
|
|
}
|
|
}
|
|
|
|
void simple_wallet::show_message(const mms::message &m)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
|
|
bool display_content;
|
|
std::string sanitized_text;
|
|
switch (m.type)
|
|
{
|
|
case mms::message_type::key_set:
|
|
case mms::message_type::additional_key_set:
|
|
case mms::message_type::note:
|
|
display_content = true;
|
|
ms.get_sanitized_message_text(m, sanitized_text);
|
|
break;
|
|
default:
|
|
display_content = false;
|
|
}
|
|
uint64_t now = (uint64_t)time(NULL);
|
|
message_writer() << "";
|
|
message_writer() << tr("Message ") << m.id;
|
|
message_writer() << tr("In/out: ") << ms.message_direction_to_string(m.direction);
|
|
message_writer() << tr("Type: ") << ms.message_type_to_string(m.type);
|
|
message_writer() << tr("State: ") << boost::format(tr("%s since %s, %s ago")) %
|
|
ms.message_state_to_string(m.state) % tools::get_human_readable_timestamp(m.modified) % tools::get_human_readable_timespan(std::chrono::seconds(now - m.modified));
|
|
if (m.sent == 0)
|
|
{
|
|
message_writer() << tr("Sent: Never");
|
|
}
|
|
else
|
|
{
|
|
message_writer() << boost::format(tr("Sent: %s, %s ago")) %
|
|
tools::get_human_readable_timestamp(m.sent) % tools::get_human_readable_timespan(std::chrono::seconds(now - m.sent));
|
|
}
|
|
message_writer() << tr("Authorized signer: ") << ms.signer_to_string(signer, 100);
|
|
message_writer() << tr("Content size: ") << m.content.length() << tr(" bytes");
|
|
message_writer() << tr("Content: ") << (display_content ? sanitized_text : tr("(binary data)"));
|
|
|
|
if (m.type == mms::message_type::note)
|
|
{
|
|
// Showing a note and read its text is "processing" it: Set the state accordingly
|
|
// which will also delete it from Bitmessage as a side effect
|
|
// (Without this little "twist" it would never change the state, and never get deleted)
|
|
ms.set_message_processed_or_sent(m.id);
|
|
}
|
|
}
|
|
|
|
void simple_wallet::ask_send_all_ready_messages()
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
std::vector<mms::message> ready_messages;
|
|
const std::vector<mms::message> &messages = ms.get_all_messages();
|
|
for (size_t i = 0; i < messages.size(); ++i)
|
|
{
|
|
const mms::message &m = messages[i];
|
|
if (m.state == mms::message_state::ready_to_send)
|
|
{
|
|
ready_messages.push_back(m);
|
|
}
|
|
}
|
|
if (ready_messages.size() != 0)
|
|
{
|
|
list_mms_messages(ready_messages);
|
|
bool send = ms.get_auto_send();
|
|
if (!send)
|
|
{
|
|
send = user_confirms(tr("Send these messages now?"));
|
|
}
|
|
if (send)
|
|
{
|
|
mms::multisig_wallet_state state = get_multisig_wallet_state();
|
|
for (size_t i = 0; i < ready_messages.size(); ++i)
|
|
{
|
|
ms.send_message(state, ready_messages[i].id);
|
|
ms.set_message_processed_or_sent(ready_messages[i].id);
|
|
}
|
|
success_msg_writer() << tr("Queued for sending.");
|
|
}
|
|
}
|
|
}
|
|
|
|
bool simple_wallet::get_message_from_arg(const std::string &arg, mms::message &m)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
bool valid_id = false;
|
|
uint32_t id;
|
|
try
|
|
{
|
|
id = (uint32_t)boost::lexical_cast<uint32_t>(arg);
|
|
valid_id = ms.get_message_by_id(id, m);
|
|
}
|
|
catch (const boost::bad_lexical_cast &)
|
|
{
|
|
}
|
|
if (!valid_id)
|
|
{
|
|
fail_msg_writer() << tr("Invalid message id");
|
|
}
|
|
return valid_id;
|
|
}
|
|
|
|
void simple_wallet::mms_init(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 3)
|
|
{
|
|
fail_msg_writer() << tr("usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
|
|
return;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (ms.get_active())
|
|
{
|
|
if (!user_confirms(tr("The MMS is already initialized. Re-initialize by deleting all signer info and messages?")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
uint32_t num_required_signers;
|
|
uint32_t num_authorized_signers;
|
|
const std::string &mn = args[0];
|
|
std::vector<std::string> numbers;
|
|
boost::split(numbers, mn, boost::is_any_of("/"));
|
|
bool mn_ok = (numbers.size() == 2)
|
|
&& get_number_from_arg(numbers[1], num_authorized_signers, 2, 100)
|
|
&& get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers);
|
|
if (!mn_ok)
|
|
{
|
|
fail_msg_writer() << tr("Error in the number of required signers and/or authorized signers");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
ms.init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers);
|
|
}
|
|
|
|
void simple_wallet::mms_info(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (ms.get_active())
|
|
{
|
|
message_writer() << boost::format("The MMS is active for %s/%s multisig.")
|
|
% ms.get_num_required_signers() % ms.get_num_authorized_signers();
|
|
}
|
|
else
|
|
{
|
|
message_writer() << tr("The MMS is not active.");
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_signer(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
const std::vector<mms::authorized_signer> &signers = ms.get_all_signers();
|
|
if (args.size() == 0)
|
|
{
|
|
// Without further parameters list all defined signers
|
|
list_signers(signers);
|
|
return;
|
|
}
|
|
|
|
uint32_t index;
|
|
bool index_valid = get_number_from_arg(args[0], index, 1, ms.get_num_authorized_signers());
|
|
if (index_valid)
|
|
{
|
|
index--;
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Invalid signer number ") + args[0];
|
|
return;
|
|
}
|
|
if ((args.size() < 2) || (args.size() > 4))
|
|
{
|
|
fail_msg_writer() << tr("mms signer [<number> <label> [<transport_address> [<oxen_address>]]]");
|
|
return;
|
|
}
|
|
|
|
std::optional<std::string> label = args[1];
|
|
std::optional<std::string> transport_address;
|
|
if (args.size() >= 3)
|
|
{
|
|
transport_address = args[2];
|
|
}
|
|
std::optional<cryptonote::account_public_address> monero_address;
|
|
LOCK_IDLE_SCOPE();
|
|
mms::multisig_wallet_state state = get_multisig_wallet_state();
|
|
if (args.size() == 4)
|
|
{
|
|
cryptonote::address_parse_info info;
|
|
bool ok = cryptonote::get_account_address_from_str(info, m_wallet->nettype(), args[3]);
|
|
if (!ok)
|
|
{
|
|
fail_msg_writer() << tr("Invalid Oxen address");
|
|
return;
|
|
}
|
|
monero_address = info.address;
|
|
const std::vector<mms::message> &messages = ms.get_all_messages();
|
|
if ((messages.size() > 0) || state.multisig)
|
|
{
|
|
fail_msg_writer() << tr("Wallet state does not allow changing Oxen addresses anymore");
|
|
return;
|
|
}
|
|
}
|
|
ms.set_signer(state, index, label, transport_address, monero_address);
|
|
}
|
|
|
|
void simple_wallet::mms_list(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (args.size() != 0)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms list");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
const std::vector<mms::message> &messages = ms.get_all_messages();
|
|
list_mms_messages(messages);
|
|
}
|
|
|
|
void simple_wallet::mms_next(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if ((args.size() > 1) || ((args.size() == 1) && (args[0] != "sync")))
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms next [sync]");
|
|
return;
|
|
}
|
|
bool avail = false;
|
|
std::vector<mms::processing_data> data_list;
|
|
bool force_sync = false;
|
|
uint32_t choice = 0;
|
|
{
|
|
LOCK_IDLE_SCOPE();
|
|
if ((args.size() == 1) && (args[0] == "sync"))
|
|
{
|
|
// Force the MMS to process any waiting sync info although on its own it would just ignore
|
|
// those messages because no need to process them can be seen
|
|
force_sync = true;
|
|
}
|
|
std::string wait_reason;
|
|
{
|
|
avail = ms.get_processable_messages(get_multisig_wallet_state(), force_sync, data_list, wait_reason);
|
|
}
|
|
if (avail)
|
|
{
|
|
avail = choose_mms_processing(data_list, choice);
|
|
}
|
|
else if (!wait_reason.empty())
|
|
{
|
|
message_writer() << tr("No next step: ") << wait_reason;
|
|
}
|
|
}
|
|
if (avail)
|
|
{
|
|
mms::processing_data data = data_list[choice];
|
|
bool command_successful = false;
|
|
switch(data.processing)
|
|
{
|
|
case mms::message_processing::prepare_multisig:
|
|
message_writer() << tr("prepare_multisig");
|
|
command_successful = prepare_multisig_main(std::vector<std::string>(), true);
|
|
break;
|
|
|
|
case mms::message_processing::make_multisig:
|
|
{
|
|
message_writer() << tr("make_multisig");
|
|
size_t number_of_key_sets = data.message_ids.size();
|
|
std::vector<std::string> sig_args(number_of_key_sets + 1);
|
|
sig_args[0] = std::to_string(ms.get_num_required_signers());
|
|
for (size_t i = 0; i < number_of_key_sets; ++i)
|
|
{
|
|
mms::message m = ms.get_message_by_id(data.message_ids[i]);
|
|
sig_args[i+1] = m.content;
|
|
}
|
|
command_successful = make_multisig_main(sig_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::exchange_multisig_keys:
|
|
{
|
|
message_writer() << tr("exchange_multisig_keys");
|
|
size_t number_of_key_sets = data.message_ids.size();
|
|
// Other than "make_multisig" only the key sets as parameters, no num_required_signers
|
|
std::vector<std::string> sig_args(number_of_key_sets);
|
|
for (size_t i = 0; i < number_of_key_sets; ++i)
|
|
{
|
|
mms::message m = ms.get_message_by_id(data.message_ids[i]);
|
|
sig_args[i] = m.content;
|
|
}
|
|
command_successful = exchange_multisig_keys_main(sig_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::create_sync_data:
|
|
{
|
|
message_writer() << tr("export_multisig_info");
|
|
std::vector<std::string> export_args;
|
|
export_args.push_back("MMS"); // dummy filename
|
|
command_successful = export_multisig_main(export_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::process_sync_data:
|
|
{
|
|
message_writer() << tr("import_multisig_info");
|
|
std::vector<std::string> import_args;
|
|
for (size_t i = 0; i < data.message_ids.size(); ++i)
|
|
{
|
|
mms::message m = ms.get_message_by_id(data.message_ids[i]);
|
|
import_args.push_back(m.content);
|
|
}
|
|
command_successful = import_multisig_main(import_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::sign_tx:
|
|
{
|
|
message_writer() << tr("sign_multisig");
|
|
std::vector<std::string> sign_args;
|
|
mms::message m = ms.get_message_by_id(data.message_ids[0]);
|
|
sign_args.push_back(m.content);
|
|
command_successful = sign_multisig_main(sign_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::submit_tx:
|
|
{
|
|
message_writer() << tr("submit_multisig");
|
|
std::vector<std::string> submit_args;
|
|
mms::message m = ms.get_message_by_id(data.message_ids[0]);
|
|
submit_args.push_back(m.content);
|
|
command_successful = submit_multisig_main(submit_args, true);
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::send_tx:
|
|
{
|
|
message_writer() << tr("Send tx");
|
|
mms::message m = ms.get_message_by_id(data.message_ids[0]);
|
|
LOCK_IDLE_SCOPE();
|
|
ms.add_message(get_multisig_wallet_state(), data.receiving_signer_index, m.type, mms::message_direction::out,
|
|
m.content);
|
|
command_successful = true;
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::process_signer_config:
|
|
{
|
|
message_writer() << tr("Process signer config");
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message m = ms.get_message_by_id(data.message_ids[0]);
|
|
mms::authorized_signer me = ms.get_signer(0);
|
|
mms::multisig_wallet_state state = get_multisig_wallet_state();
|
|
if (!me.auto_config_running)
|
|
{
|
|
// If no auto-config is running, the config sent may be unsolicited or problematic
|
|
// so show what arrived and ask for confirmation before taking it in
|
|
std::vector<mms::authorized_signer> signers;
|
|
ms.unpack_signer_config(state, m.content, signers);
|
|
list_signers(signers);
|
|
if (!user_confirms(tr("Replace current signer config with the one displayed above?")))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
ms.process_signer_config(state, m.content);
|
|
ms.stop_auto_config();
|
|
list_signers(ms.get_all_signers());
|
|
command_successful = true;
|
|
break;
|
|
}
|
|
|
|
case mms::message_processing::process_auto_config_data:
|
|
{
|
|
message_writer() << tr("Process auto config data");
|
|
LOCK_IDLE_SCOPE();
|
|
for (size_t i = 0; i < data.message_ids.size(); ++i)
|
|
{
|
|
ms.process_auto_config_data_message(data.message_ids[i]);
|
|
}
|
|
ms.stop_auto_config();
|
|
list_signers(ms.get_all_signers());
|
|
add_signer_config_messages();
|
|
command_successful = true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
message_writer() << tr("Nothing ready to process");
|
|
break;
|
|
}
|
|
|
|
if (command_successful)
|
|
{
|
|
{
|
|
LOCK_IDLE_SCOPE();
|
|
ms.set_messages_processed(data);
|
|
ask_send_all_ready_messages();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_sync(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (args.size() != 0)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms sync");
|
|
return;
|
|
}
|
|
// Force the start of a new sync round, for exceptional cases where something went wrong
|
|
// Can e.g. solve the problem "This signature was made with stale data" after trying to
|
|
// create 2 transactions in a row somehow
|
|
// Code is identical to the code for 'message_processing::create_sync_data'
|
|
message_writer() << tr("export_multisig_info");
|
|
std::vector<std::string> export_args;
|
|
export_args.push_back("MMS"); // dummy filename
|
|
export_multisig_main(export_args, true);
|
|
ask_send_all_ready_messages();
|
|
}
|
|
|
|
void simple_wallet::mms_transfer(const std::vector<std::string> &args)
|
|
{
|
|
// It's too complicated to check any arguments here, just let 'transfer_main' do the whole job
|
|
transfer_main(Transfer::Normal, args, true);
|
|
}
|
|
|
|
void simple_wallet::mms_delete(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms delete (<message_id> | all)");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (args[0] == "all")
|
|
{
|
|
if (user_confirms(tr("Delete all messages?")))
|
|
{
|
|
ms.delete_all_messages();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mms::message m;
|
|
bool valid_id = get_message_from_arg(args[0], m);
|
|
if (valid_id)
|
|
{
|
|
// If only a single message and not all delete even if unsent / unprocessed
|
|
ms.delete_message(m.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_send(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() == 0)
|
|
{
|
|
ask_send_all_ready_messages();
|
|
return;
|
|
}
|
|
else if (args.size() != 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms send [<message_id>]");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
mms::message m;
|
|
bool valid_id = get_message_from_arg(args[0], m);
|
|
if (valid_id)
|
|
{
|
|
ms.send_message(get_multisig_wallet_state(), m.id);
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_receive(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 0)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms receive");
|
|
return;
|
|
}
|
|
std::vector<mms::message> new_messages;
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
bool avail = ms.check_for_messages(get_multisig_wallet_state(), new_messages);
|
|
if (avail)
|
|
{
|
|
list_mms_messages(new_messages);
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_export(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms export <message_id>");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
mms::message m;
|
|
bool valid_id = get_message_from_arg(args[0], m);
|
|
if (valid_id)
|
|
{
|
|
fs::path filename = fs::u8path("mms_message_content");
|
|
if (m_wallet->save_to_file(filename, m.content))
|
|
{
|
|
success_msg_writer() << tr("Message content saved to: ") << filename;
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Failed to to save message content");
|
|
}
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_note(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (args.size() == 0)
|
|
{
|
|
LOCK_IDLE_SCOPE();
|
|
const std::vector<mms::message> &messages = ms.get_all_messages();
|
|
for (size_t i = 0; i < messages.size(); ++i)
|
|
{
|
|
const mms::message &m = messages[i];
|
|
if ((m.type == mms::message_type::note) && (m.state == mms::message_state::waiting))
|
|
{
|
|
show_message(m);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (args.size() < 2)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms note [<label> <text>]");
|
|
return;
|
|
}
|
|
uint32_t signer_index;
|
|
bool found = ms.get_signer_index_by_label(args[0], signer_index);
|
|
if (!found)
|
|
{
|
|
fail_msg_writer() << tr("No signer found with label ") << args[0];
|
|
return;
|
|
}
|
|
std::string note = "";
|
|
for (size_t n = 1; n < args.size(); ++n)
|
|
{
|
|
if (n > 1)
|
|
{
|
|
note += " ";
|
|
}
|
|
note += args[n];
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
ms.add_message(get_multisig_wallet_state(), signer_index, mms::message_type::note,
|
|
mms::message_direction::out, note);
|
|
ask_send_all_ready_messages();
|
|
}
|
|
|
|
void simple_wallet::mms_show(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms show <message_id>");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
mms::message m;
|
|
bool valid_id = get_message_from_arg(args[0], m);
|
|
if (valid_id)
|
|
{
|
|
show_message(m);
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_set(const std::vector<std::string> &args)
|
|
{
|
|
bool set = args.size() == 2;
|
|
bool query = args.size() == 1;
|
|
if (!set && !query)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms set <option_name> [<option_value>]");
|
|
return;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
LOCK_IDLE_SCOPE();
|
|
if (args[0] == "auto-send")
|
|
{
|
|
if (set)
|
|
{
|
|
bool result;
|
|
bool ok = parse_bool(args[1], result);
|
|
if (ok)
|
|
{
|
|
ms.set_auto_send(result);
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Wrong option value");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message_writer() << (ms.get_auto_send() ? tr("Auto-send is on") : tr("Auto-send is off"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Unknown option");
|
|
}
|
|
}
|
|
|
|
void simple_wallet::mms_help(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() > 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms help [<subcommand>]");
|
|
return;
|
|
}
|
|
std::vector<std::string> help_args;
|
|
help_args.push_back("mms");
|
|
if (args.size() == 1)
|
|
{
|
|
help_args.push_back(args[0]);
|
|
}
|
|
help(help_args);
|
|
}
|
|
|
|
void simple_wallet::mms_send_signer_config(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 0)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms send_signer_config");
|
|
return;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (!ms.signer_config_complete())
|
|
{
|
|
fail_msg_writer() << tr("Signer config not yet complete");
|
|
return;
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
add_signer_config_messages();
|
|
ask_send_all_ready_messages();
|
|
}
|
|
|
|
void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args)
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
uint32_t other_signers = ms.get_num_authorized_signers() - 1;
|
|
size_t args_size = args.size();
|
|
if ((args_size != 0) && (args_size != other_signers))
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms start_auto_config [<label> <label> ...]");
|
|
return;
|
|
}
|
|
if ((args_size == 0) && !ms.signer_labels_complete())
|
|
{
|
|
fail_msg_writer() << tr("There are signers without a label set. Complete labels before auto-config or specify them as parameters here.");
|
|
return;
|
|
}
|
|
mms::authorized_signer me = ms.get_signer(0);
|
|
if (me.auto_config_running)
|
|
{
|
|
if (!user_confirms(tr("Auto-config is already running. Cancel and restart?")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
mms::multisig_wallet_state state = get_multisig_wallet_state();
|
|
if (args_size != 0)
|
|
{
|
|
// Set (or overwrite) all the labels except "me" from the arguments
|
|
for (uint32_t i = 1; i < (other_signers + 1); ++i)
|
|
{
|
|
ms.set_signer(state, i, args[i - 1], std::nullopt, std::nullopt);
|
|
}
|
|
}
|
|
ms.start_auto_config(state);
|
|
// List the signers to show the generated auto-config tokens
|
|
list_signers(ms.get_all_signers());
|
|
}
|
|
|
|
void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 0)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms stop_auto_config");
|
|
return;
|
|
}
|
|
if (!user_confirms(tr("Delete any auto-config tokens and stop auto-config?")))
|
|
{
|
|
return;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
LOCK_IDLE_SCOPE();
|
|
ms.stop_auto_config();
|
|
}
|
|
|
|
void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
|
|
{
|
|
if (args.size() != 1)
|
|
{
|
|
fail_msg_writer() << tr("Usage: mms auto_config <auto_config_token>");
|
|
return;
|
|
}
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
std::string adjusted_token;
|
|
if (!ms.check_auto_config_token(args[0], adjusted_token))
|
|
{
|
|
fail_msg_writer() << tr("Invalid auto-config token");
|
|
return;
|
|
}
|
|
mms::authorized_signer me = ms.get_signer(0);
|
|
if (me.auto_config_running)
|
|
{
|
|
if (!user_confirms(tr("Auto-config already running. Cancel and restart?")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
LOCK_IDLE_SCOPE();
|
|
ms.add_auto_config_data_message(get_multisig_wallet_state(), adjusted_token);
|
|
ask_send_all_ready_messages();
|
|
}
|
|
|
|
bool simple_wallet::mms(const std::vector<std::string> &args)
|
|
{
|
|
try
|
|
{
|
|
m_wallet->get_multisig_wallet_state();
|
|
}
|
|
catch(const std::exception &e)
|
|
{
|
|
fail_msg_writer() << tr("MMS not available in this wallet");
|
|
return true;
|
|
}
|
|
|
|
try
|
|
{
|
|
mms::message_store& ms = m_wallet->get_message_store();
|
|
if (args.size() == 0)
|
|
{
|
|
mms_info(args);
|
|
return true;
|
|
}
|
|
|
|
const std::string &sub_command = args[0];
|
|
std::vector<std::string> mms_args = args;
|
|
mms_args.erase(mms_args.begin());
|
|
|
|
if (sub_command == "init")
|
|
{
|
|
mms_init(mms_args);
|
|
return true;
|
|
}
|
|
if (!ms.get_active())
|
|
{
|
|
fail_msg_writer() << tr("The MMS is not active. Activate using the \"mms init\" command");
|
|
return true;
|
|
}
|
|
else if (sub_command == "info")
|
|
{
|
|
mms_info(mms_args);
|
|
}
|
|
else if (sub_command == "signer")
|
|
{
|
|
mms_signer(mms_args);
|
|
}
|
|
else if (sub_command == "list")
|
|
{
|
|
mms_list(mms_args);
|
|
}
|
|
else if (sub_command == "next")
|
|
{
|
|
mms_next(mms_args);
|
|
}
|
|
else if (sub_command == "sync")
|
|
{
|
|
mms_sync(mms_args);
|
|
}
|
|
else if (sub_command == "transfer")
|
|
{
|
|
mms_transfer(mms_args);
|
|
}
|
|
else if (sub_command == "delete")
|
|
{
|
|
mms_delete(mms_args);
|
|
}
|
|
else if (sub_command == "send")
|
|
{
|
|
mms_send(mms_args);
|
|
}
|
|
else if (sub_command == "receive")
|
|
{
|
|
mms_receive(mms_args);
|
|
}
|
|
else if (sub_command == "export")
|
|
{
|
|
mms_export(mms_args);
|
|
}
|
|
else if (sub_command == "note")
|
|
{
|
|
mms_note(mms_args);
|
|
}
|
|
else if (sub_command == "show")
|
|
{
|
|
mms_show(mms_args);
|
|
}
|
|
else if (sub_command == "set")
|
|
{
|
|
mms_set(mms_args);
|
|
}
|
|
else if (sub_command == "help")
|
|
{
|
|
mms_help(mms_args);
|
|
}
|
|
else if (sub_command == "send_signer_config")
|
|
{
|
|
mms_send_signer_config(mms_args);
|
|
}
|
|
else if (sub_command == "start_auto_config")
|
|
{
|
|
mms_start_auto_config(mms_args);
|
|
}
|
|
else if (sub_command == "stop_auto_config")
|
|
{
|
|
mms_stop_auto_config(mms_args);
|
|
}
|
|
else if (sub_command == "auto_config")
|
|
{
|
|
mms_auto_config(mms_args);
|
|
}
|
|
else
|
|
{
|
|
fail_msg_writer() << tr("Invalid MMS subcommand");
|
|
}
|
|
}
|
|
catch (const tools::error::no_connection_to_daemon &e)
|
|
{
|
|
fail_msg_writer() << tr("Error in MMS command: ") << e.what() << " " << e.request();
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
fail_msg_writer() << tr("Error in MMS command: ") << e.what();
|
|
PRINT_USAGE(USAGE_MMS);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
// End MMS ------------------------------------------------------------------------------------------------
|