oxen-core/src/simplewallet/simplewallet-mms.inl

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 ------------------------------------------------------------------------------------------------