Electroneum
Loading...
Searching...
No Matches
message_store.cpp
Go to the documentation of this file.
1// Copyright (c) 2018, The Monero Project
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "message_store.h"
32#include <boost/format.hpp>
33#include <boost/algorithm/string.hpp>
34#include <fstream>
35#include <sstream>
36#include "file_io_utils.h"
38#include "wallet_errors.h"
40#include "common/base58.h"
41#include "common/util.h"
42#include "string_tools.h"
43
44
45#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
46#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.mms"
47
48namespace mms
49{
50
52{
53 m_active = false;
54 m_auto_send = false;
55 m_next_message_id = 1;
56 m_num_authorized_signers = 0;
57 m_num_required_signers = 0;
59 m_run = true;
60}
61
62namespace
63{
64 // MMS options handling mirrors what "wallet2" is doing for its options, on-demand init and all
65 // It's not very clean to initialize Bitmessage-specific options here, but going one level further
66 // down still into "message_transporter" for that is a little bit too much
67 struct options
68 {
69 const command_line::arg_descriptor<std::string> bitmessage_address = {"bitmessage-address", mms::message_store::tr("Use PyBitmessage instance at URL <arg>"), "http://localhost:8442/"};
70 const command_line::arg_descriptor<std::string> bitmessage_login = {"bitmessage-login", mms::message_store::tr("Specify <arg> as username:password for PyBitmessage API"), "username:password"};
71 };
72}
73
74void message_store::init_options(boost::program_options::options_description& desc_params)
75{
76 const options opts{};
77 command_line::add_arg(desc_params, opts.bitmessage_address);
78 command_line::add_arg(desc_params, opts.bitmessage_login);
79}
80
81void message_store::init(const multisig_wallet_state &state, const std::string &own_label,
82 const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers)
83{
84 m_num_authorized_signers = num_authorized_signers;
85 m_num_required_signers = num_required_signers;
86 m_signers.clear();
87 m_messages.clear();
88 m_next_message_id = 1;
89
90 // The vector "m_signers" gets here once the required number of elements, one for each authorized signer,
91 // and is never changed again. The rest of the code relies on "size(m_signers) == m_num_authorized_signers"
92 // without further checks.
93 authorized_signer signer;
94 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
95 {
96 signer.me = signer.index == 0; // Strict convention: The very first signer is fixed as / must be "me"
97 m_signers.push_back(signer);
98 signer.index++;
99 }
100
101 set_signer(state, 0, own_label, own_transport_address, state.address);
102
103 m_nettype = state.nettype;
104 set_active(true);
105 m_filename = state.mms_file;
106 save(state);
107}
108
109void message_store::set_options(const boost::program_options::variables_map& vm)
110{
111 const options opts{};
112 std::string bitmessage_address = command_line::get_arg(vm, opts.bitmessage_address);
113 epee::wipeable_string bitmessage_login = command_line::get_arg(vm, opts.bitmessage_login);
114 set_options(bitmessage_address, bitmessage_login);
115}
116
117void message_store::set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login)
118{
119 m_transporter.set_options(bitmessage_address, bitmessage_login);
120}
121
123 uint32_t index,
124 const boost::optional<std::string> &label,
125 const boost::optional<std::string> &transport_address,
126 const boost::optional<cryptonote::account_public_address> etn_address)
127{
128 THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index));
129 authorized_signer &m = m_signers[index];
130 if (label)
131 {
132 m.label = label.get();
133 }
134 if (transport_address)
135 {
136 m.transport_address = transport_address.get();
137 }
138 if (etn_address)
139 {
140 m.etn_address_known = true;
141 m.etn_address = etn_address.get();
142 }
143 // Save to minimize the chance to loose that info (at least while in beta)
144 save(state);
145}
146
148{
149 THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index));
150 return m_signers[index];
151}
152
154{
155 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
156 {
157 const authorized_signer &m = m_signers[i];
158 if (m.label.empty() || m.transport_address.empty() || !m.etn_address_known)
159 {
160 return false;
161 }
162 }
163 return true;
164}
165
166// Check if all signers have a label set (as it's a requirement for starting auto-config
167// by the "manager")
169{
170 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
171 {
172 const authorized_signer &m = m_signers[i];
173 if (m.label.empty())
174 {
175 return false;
176 }
177 }
178 return true;
179}
180
182{
183 std::stringstream oss;
185 ar << m_signers;
186 signer_config = oss.str();
187}
188
190 std::vector<authorized_signer> &signers)
191{
192 try
193 {
194 std::stringstream iss;
195 iss << signer_config;
197 ar >> signers;
198 }
199 catch (...)
200 {
201 THROW_WALLET_EXCEPTION_IF(true, tools::error::wallet_internal_error, "Invalid structure of signer config");
202 }
203 uint32_t num_signers = (uint32_t)signers.size();
204 THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers));
205}
206
208{
209 // The signers in "signer_config" and the resident wallet signers are matched not by label, but
210 // by Electroneum address, and ALL labels will be set from "signer_config", even the "me" label.
211 // In the auto-config process as implemented now the auto-config manager is responsible for defining
212 // the labels, and right at the end of the process ALL wallets use the SAME labels. The idea behind this
213 // is preventing problems like duplicate labels and confusion (Bob choosing a label "IamAliceHonest").
214 // (Of course signers are free to re-define any labels they don't like AFTER auto-config.)
215 //
216 // Usually this method will be called with only the "me" signer defined in the wallet, and may
217 // produce unexpected behaviour if that wallet contains additional signers that have nothing to do with
218 // those arriving in "signer_config".
219 std::vector<authorized_signer> signers;
221
222 uint32_t new_index = 1;
223 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
224 {
225 const authorized_signer &m = signers[i];
226 uint32_t index;
227 uint32_t take_index;
228 bool found = get_signer_index_by_etn_address(m.etn_address, index);
229 if (found)
230 {
231 // Redefine existing (probably "me", under usual circumstances)
232 take_index = index;
233 }
234 else
235 {
236 // Add new; neglect that we may erroneously overwrite already defined signers
237 // (but protect "me")
238 take_index = new_index;
239 if ((new_index + 1) < m_num_authorized_signers)
240 {
241 new_index++;
242 }
243 }
244 authorized_signer &modify = m_signers[take_index];
245 modify.label = m.label; // ALWAYS set label, see comments above
246 if (!modify.me)
247 {
250 if (m.etn_address_known)
251 {
252 modify.etn_address = m.etn_address;
253 }
254 }
255 }
256 save(state);
257}
258
260{
261 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
262 {
263 authorized_signer &m = m_signers[i];
264 if (!m.me)
265 {
266 setup_signer_for_auto_config(i, create_auto_config_token(), true);
267 }
268 m.auto_config_running = true;
269 }
270 save(state);
271}
272
273// Check auto-config token string and convert to standardized form;
274// Try to make it as foolproof as possible, with built-in tolerance to make up for
275// errors in transmission that still leave the token recognizable.
276bool message_store::check_auto_config_token(const std::string &raw_token,
277 std::string &adjusted_token) const
278{
279 std::string prefix(AUTO_CONFIG_TOKEN_PREFIX);
280 uint32_t num_hex_digits = (AUTO_CONFIG_TOKEN_BYTES + 1) * 2;
281 uint32_t full_length = num_hex_digits + prefix.length();
282 uint32_t raw_length = raw_token.length();
283 std::string hex_digits;
284
285 if (raw_length == full_length)
286 {
287 // Prefix must be there; accept it in any casing
288 std::string raw_prefix(raw_token.substr(0, 3));
289 boost::algorithm::to_lower(raw_prefix);
290 if (raw_prefix != prefix)
291 {
292 return false;
293 }
294 hex_digits = raw_token.substr(3);
295 }
296 else if (raw_length == num_hex_digits)
297 {
298 // Accept the token without the prefix if it's otherwise ok
299 hex_digits = raw_token;
300 }
301 else
302 {
303 return false;
304 }
305
306 // Convert to strict lowercase and correct any common misspellings
307 boost::algorithm::to_lower(hex_digits);
308 std::replace(hex_digits.begin(), hex_digits.end(), 'o', '0');
309 std::replace(hex_digits.begin(), hex_digits.end(), 'i', '1');
310 std::replace(hex_digits.begin(), hex_digits.end(), 'l', '1');
311
312 // Now it must be correct hex with correct checksum, no further tolerance possible
313 std::string token_bytes;
314 if (!epee::string_tools::parse_hexstr_to_binbuff(hex_digits, token_bytes))
315 {
316 return false;
317 }
318 const crypto::hash &hash = crypto::cn_fast_hash(token_bytes.data(), token_bytes.size() - 1);
319 if (token_bytes[AUTO_CONFIG_TOKEN_BYTES] != hash.data[0])
320 {
321 return false;
322 }
323 adjusted_token = prefix + hex_digits;
324 return true;
325}
326
327// Create a new auto-config token with prefix, random 8-hex digits plus 2 checksum digits
328std::string message_store::create_auto_config_token()
329{
330 unsigned char random[AUTO_CONFIG_TOKEN_BYTES];
332 std::string token_bytes;
333 token_bytes.append((char *)random, AUTO_CONFIG_TOKEN_BYTES);
334
335 // Add a checksum because technically ANY four bytes are a valid token, and without a checksum we would send
336 // auto-config messages "to nowhere" after the slightest typo without knowing it
337 const crypto::hash &hash = crypto::cn_fast_hash(token_bytes.data(), token_bytes.size());
338 token_bytes += hash.data[0];
339 std::string prefix(AUTO_CONFIG_TOKEN_PREFIX);
340 return prefix + epee::string_tools::buff_to_hex_nodelimer(token_bytes);
341}
342
343// Add a message for sending "me" address data to the auto-config transport address
344// that can be derived from the token and activate auto-config
346 const std::string &auto_config_token)
347{
348 authorized_signer &me = m_signers[0];
349 me.auto_config_token = auto_config_token;
350 setup_signer_for_auto_config(0, auto_config_token, false);
351 me.auto_config_running = true;
352
353 auto_config_data data;
354 data.label = me.label;
356 data.etn_address = me.etn_address;
357
358 std::stringstream oss;
360 ar << data;
361
363}
364
365// Process a single message with auto-config data, destined for "message.signer_index"
367{
368 // "auto_config_data" contains the label that the auto-config data sender uses for "me", but that's
369 // more for completeness' sake, and right now it's not used. In general, the auto-config manager
370 // decides/defines the labels, and right after completing auto-config ALL wallets use the SAME labels.
371
372 const message &m = get_message_ref_by_id(id);
373
374 auto_config_data data;
375 try
376 {
377 std::stringstream iss;
378 iss << m.content;
380 ar >> data;
381 }
382 catch (...)
383 {
384 THROW_WALLET_EXCEPTION_IF(true, tools::error::wallet_internal_error, "Invalid structure of auto config data");
385 }
386
387 authorized_signer &signer = m_signers[m.signer_index];
388 // "signer.label" does NOT change, see comment above
390 signer.etn_address_known = true;
391 signer.etn_address = data.etn_address;
392 signer.auto_config_running = false;
393}
394
396{
397 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
398 {
399 authorized_signer &m = m_signers[i];
400 if (!m.me && !m.auto_config_transport_address.empty())
401 {
402 // Try to delete those "unused API" addresses in PyBitmessage, especially since
403 // it seems it's not possible to delete them interactively, only to "disable" them
404 m_transporter.delete_transport_address(m.auto_config_transport_address);
405 }
406 m.auto_config_token.clear();
410 m.auto_config_running = false;
411 }
412}
413
414void message_store::setup_signer_for_auto_config(uint32_t index, const std::string token, bool receiving)
415{
416 // It may be a little strange to hash the textual hex digits of the auto config token into
417 // 32 bytes and turn that into a Electroneum public/secret key pair, instead of doing something
418 // much less complicated like directly using the underlying random 40 bits as key for a
419 // symmetric cipher, but everything is there already for encrypting and decrypting messages
420 // with such key pairs, and furthermore it would be trivial to use tokens with a different
421 // number of bytes.
422 //
423 // In the wallet of the auto-config manager each signer except "me" gets set its own
424 // auto-config parameters. In the wallet of somebody using the token to send auto-config
425 // data the auto-config parameters are stored in the "me" signer and taken from there
426 // to send that data.
427 THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index));
428 authorized_signer &m = m_signers[index];
429 m.auto_config_token = token;
430 crypto::hash_to_scalar(token.data(), token.size(), m.auto_config_secret_key);
432 if (receiving)
433 {
435 }
436 else
437 {
439 }
440}
441
443{
444 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
445 {
446 const authorized_signer &m = m_signers[i];
447 if (m.etn_address == etn_address)
448 {
449 index = m.index;
450 return true;
451 }
452 }
453 MWARNING("No authorized signer with Electroneum address " << account_address_to_string(etn_address));
454 return false;
455}
456
457bool message_store::get_signer_index_by_label(const std::string label, uint32_t &index) const
458{
459 for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
460 {
461 const authorized_signer &m = m_signers[i];
462 if (m.label == label)
463 {
464 index = m.index;
465 return true;
466 }
467 }
468 MWARNING("No authorized signer with label " << label);
469 return false;
470}
471
473{
474 switch(type)
475 {
477 // Result of a "prepare_multisig" command in the wallet
478 // Send the key set to all other signers
480 // Result of a "make_multisig" command or a "exchange_multisig_keys" in the wallet in case of M/N multisig
481 // Send the additional key set to all other signers
483 // Result of a "export_multisig_info" command in the wallet
484 // Send the sync data to all other signers
485 for (uint32_t i = 1; i < m_num_authorized_signers; ++i)
486 {
487 add_message(state, i, type, message_direction::out, content);
488 }
489 break;
490
492 // Result of a "transfer" command in the wallet, or a "sign_multisig" command
493 // that did not yet result in the minimum number of signatures required
494 // Create a message "from me to me" as a container for the tx data
495 if (m_num_required_signers == 1)
496 {
497 // Probably rare, but possible: The 1 signature is already enough, correct the type
498 // Easier to correct here than asking all callers to detect this rare special case
500 }
501 add_message(state, 0, type, message_direction::in, content);
502 break;
503
505 add_message(state, 0, type, message_direction::in, content);
506 break;
507
508 default:
509 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Illegal message type " + std::to_string((uint32_t)type));
510 break;
511 }
512}
513
515 uint32_t signer_index, message_type type, message_direction direction,
516 const std::string &content)
517{
518 message m;
519 m.id = m_next_message_id++;
520 m.type = type;
521 m.direction = direction;
522 m.content = content;
523 m.created = (uint64_t)time(NULL);
524 m.modified = m.created;
525 m.sent = 0;
526 m.signer_index = signer_index;
527 if (direction == message_direction::out)
528 {
530 }
531 else
532 {
534 };
535 m.wallet_height = (uint32_t)state.num_transfer_details;
537 {
538 m.round = state.multisig_rounds_passed;
539 }
540 else
541 {
542 m.round = 0;
543 }
544 m.signature_count = 0; // Future expansion for signature counting when signing txs
545 m.hash = crypto::null_hash;
546 m_messages.push_back(m);
547
548 // Save for every new message right away (at least while in beta)
549 save(state);
550
551 MINFO(boost::format("Added %s message %s for signer %s of type %s")
552 % message_direction_to_string(direction) % m.id % signer_index % message_type_to_string(type));
553 return m_messages.size() - 1;
554}
555
556// Get the index of the message with id "id", return false if not found
557bool message_store::get_message_index_by_id(uint32_t id, size_t &index) const
558{
559 for (size_t i = 0; i < m_messages.size(); ++i)
560 {
561 if (m_messages[i].id == id)
562 {
563 index = i;
564 return true;
565 }
566 }
567 MWARNING("No message found with an id of " << id);
568 return false;
569}
570
571// Get the index of the message with id "id" that must exist
572size_t message_store::get_message_index_by_id(uint32_t id) const
573{
574 size_t index;
575 bool found = get_message_index_by_id(id, index);
576 THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + std::to_string(id));
577 return index;
578}
579
580// Get the modifiable message with id "id" that must exist; private/internal use!
581message& message_store::get_message_ref_by_id(uint32_t id)
582{
583 return m_messages[get_message_index_by_id(id)];
584}
585
586// Get the message with id "id", return false if not found
587// This version of the method allows to check whether id is valid without triggering an error
589{
590 size_t index;
591 bool found = get_message_index_by_id(id, index);
592 if (found)
593 {
594 m = m_messages[index];
595 }
596 return found;
597}
598
599// Get the message with id "id" that must exist
601{
602 message m;
603 bool found = get_message_by_id(id, m);
604 THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + std::to_string(id));
605 return m;
606}
607
608bool message_store::any_message_of_type(message_type type, message_direction direction) const
609{
610 for (size_t i = 0; i < m_messages.size(); ++i)
611 {
612 if ((m_messages[i].type == type) && (m_messages[i].direction == direction))
613 {
614 return true;
615 }
616 }
617 return false;
618}
619
620bool message_store::any_message_with_hash(const crypto::hash &hash) const
621{
622 for (size_t i = 0; i < m_messages.size(); ++i)
623 {
624 if (m_messages[i].hash == hash)
625 {
626 return true;
627 }
628 }
629 return false;
630}
631
632// Count the ids in the vector that are set i.e. not 0, while ignoring index 0
633// Mostly used to check whether we have a message for each authorized signer except me,
634// with the signer index used as index into 'ids'; the element at index 0, for me,
635// is ignored, to make constant subtractions of 1 for indices when filling the
636// vector unnecessary
637size_t message_store::get_other_signers_id_count(const std::vector<uint32_t> &ids) const
638{
639 size_t count = 0;
640 for (size_t i = 1 /* and not 0 */; i < ids.size(); ++i)
641 {
642 if (ids[i] != 0)
643 {
644 count++;
645 }
646 }
647 return count;
648}
649
650// Is in every element of vector 'ids' (except at index 0) a message id i.e. not 0?
651bool message_store::message_ids_complete(const std::vector<uint32_t> &ids) const
652{
653 return get_other_signers_id_count(ids) == (ids.size() - 1);
654}
655
657{
658 delete_transport_message(id);
659 size_t index = get_message_index_by_id(id);
660 m_messages.erase(m_messages.begin() + index);
661}
662
664{
665 for (size_t i = 0; i < m_messages.size(); ++i)
666 {
667 delete_transport_message(m_messages[i].id);
668 }
669 m_messages.clear();
670}
671
672// Make a message text, which is "attacker controlled data", reasonably safe to display
673// This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command
674void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const
675{
676 sanitized_text.clear();
677
678 // Restrict the size to fend of DOS-style attacks with heaps of data
679 size_t length = std::min(m.content.length(), (size_t)1000);
680
681 for (size_t i = 0; i < length; ++i)
682 {
683 char c = m.content[i];
684 if ((int)c < 32)
685 {
686 // Strip out any controls, especially ESC for getting rid of potentially dangerous
687 // ANSI escape sequences that a console window might interpret
688 c = ' ';
689 }
690 else if ((c == '<') || (c == '>'))
691 {
692 // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute
693 // when displayed in the GUI wallet
694 c = ' ';
695 }
696 sanitized_text += c;
697 }
698}
699
700void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename)
701{
702 std::stringstream oss;
704 ar << *this;
705 std::string buf = oss.str();
706
707 crypto::chacha_key key;
708 crypto::generate_chacha_key(&state.view_secret_key, sizeof(crypto::secret_key), key, 1);
709
710 file_data write_file_data = boost::value_initialized<file_data>();
711 write_file_data.magic_string = "MMS";
712 write_file_data.file_version = 0;
713 write_file_data.iv = crypto::rand<crypto::chacha_iv>();
714 std::string encrypted_data;
715 encrypted_data.resize(buf.size());
716 crypto::chacha20(buf.data(), buf.size(), key, write_file_data.iv, &encrypted_data[0]);
717 write_file_data.encrypted_data = encrypted_data;
718
719 std::stringstream file_oss;
721 file_ar << write_file_data;
722
723 bool success = epee::file_io_utils::save_string_to_file(filename, file_oss.str());
725}
726
727void message_store::read_from_file(const multisig_wallet_state &state, const std::string &filename)
728{
729 boost::system::error_code ignored_ec;
730 bool file_exists = boost::filesystem::exists(filename, ignored_ec);
731 if (!file_exists)
732 {
733 // Simply do nothing if the file is not there; allows e.g. easy recovery
734 // from problems with the MMS by deleting the file
735 MERROR("No message store file found: " << filename);
736 return;
737 }
738
739 std::string buf;
740 bool success = epee::file_io_utils::load_file_to_string(filename, buf);
742
743 file_data read_file_data;
744 try
745 {
746 std::stringstream iss;
747 iss << buf;
749 ar >> read_file_data;
750 }
751 catch (const std::exception &e)
752 {
753 MERROR("MMS file " << filename << " has bad structure <iv,encrypted_data>: " << e.what());
755 }
756
757 crypto::chacha_key key;
758 crypto::generate_chacha_key(&state.view_secret_key, sizeof(crypto::secret_key), key, 1);
759 std::string decrypted_data;
760 decrypted_data.resize(read_file_data.encrypted_data.size());
761 crypto::chacha20(read_file_data.encrypted_data.data(), read_file_data.encrypted_data.size(), key, read_file_data.iv, &decrypted_data[0]);
762
763 try
764 {
765 std::stringstream iss;
766 iss << decrypted_data;
768 ar >> *this;
769 }
770 catch (const std::exception &e)
771 {
772 MERROR("MMS file " << filename << " has bad structure: " << e.what());
774 }
775
776 m_filename = filename;
777}
778
779// Save to the same file this message store was loaded from
780// Called after changes deemed "important", to make it less probable to lose messages in case of
781// a crash; a better and long-term solution would of course be to use LMDB ...
782void message_store::save(const multisig_wallet_state &state)
783{
784 if (!m_filename.empty())
785 {
786 write_to_file(state, m_filename);
787 }
788}
789
791 bool force_sync, std::vector<processing_data> &data_list, std::string &wait_reason)
792{
793 uint32_t wallet_height = (uint32_t)state.num_transfer_details;
794 data_list.clear();
795 wait_reason.clear();
796 // In all scans over all messages looking for complete sets (1 message for each signer),
797 // if there are duplicates, the OLDEST of them is taken. This may not play a role with
798 // any of the current message types, but may with future ones, and it's probably a good
799 // idea to have a clear and somewhat defensive strategy.
800
801 std::vector<uint32_t> auto_config_messages(m_num_authorized_signers, 0);
802 bool any_auto_config = false;
803
804 for (size_t i = 0; i < m_messages.size(); ++i)
805 {
806 message &m = m_messages[i];
808 {
809 if (auto_config_messages[m.signer_index] == 0)
810 {
811 auto_config_messages[m.signer_index] = m.id;
812 any_auto_config = true;
813 }
814 // else duplicate auto config data, ignore
815 }
816 }
817
818 if (any_auto_config)
819 {
820 bool auto_config_complete = message_ids_complete(auto_config_messages);
821 if (auto_config_complete)
822 {
823 processing_data data;
825 data.message_ids = auto_config_messages;
826 data.message_ids.erase(data.message_ids.begin());
827 data_list.push_back(data);
828 return true;
829 }
830 else
831 {
832 wait_reason = tr("Auto-config cannot proceed because auto config data from other signers is not complete");
833 return false;
834 // With ANY auto config data present but not complete refuse to check for any
835 // other processing. Manually delete those messages to abort such an auto config
836 // phase if needed.
837 }
838 }
839
840 // Any signer config that arrived will be processed right away, regardless of other things that may wait
841 for (size_t i = 0; i < m_messages.size(); ++i)
842 {
843 message &m = m_messages[i];
845 {
846 processing_data data;
848 data.message_ids.push_back(m.id);
849 data_list.push_back(data);
850 return true;
851 }
852 }
853
854 // ALL of the following processings depend on the signer info being complete
856 {
857 wait_reason = tr("The signer config is not complete.");
858 return false;
859 }
860
861 if (!state.multisig)
862 {
863
864 if (!any_message_of_type(message_type::key_set, message_direction::out))
865 {
866 // With the own key set not yet ready we must do "prepare_multisig" first;
867 // Key sets from other signers may be here already, but if we process them now
868 // the wallet will go multisig too early: we can't produce our own key set any more!
869 processing_data data;
871 data_list.push_back(data);
872 return true;
873 }
874
875 // Ids of key set messages per signer index, to check completeness
876 // Naturally, does not care about the order of the messages and is trivial to secure against
877 // key sets that were received more than once
878 // With full M/N multisig now possible consider only key sets of the right round, i.e.
879 // with not yet multisig the only possible round 0
880 std::vector<uint32_t> key_set_messages(m_num_authorized_signers, 0);
881
882 for (size_t i = 0; i < m_messages.size(); ++i)
883 {
884 message &m = m_messages[i];
886 && (m.round == 0))
887 {
888 if (key_set_messages[m.signer_index] == 0)
889 {
890 key_set_messages[m.signer_index] = m.id;
891 }
892 // else duplicate key set, ignore
893 }
894 }
895
896 bool key_sets_complete = message_ids_complete(key_set_messages);
897 if (key_sets_complete)
898 {
899 // Nothing else can be ready to process earlier than this, ignore everything else and give back
900 processing_data data;
902 data.message_ids = key_set_messages;
903 data.message_ids.erase(data.message_ids.begin());
904 data_list.push_back(data);
905 return true;
906 }
907 else
908 {
909 wait_reason = tr("Wallet can't go multisig because key sets from other signers are missing or not complete.");
910 return false;
911 }
912 }
913
914 if (state.multisig && !state.multisig_is_ready)
915 {
916 // In the case of M/N multisig the call 'wallet2::multisig' returns already true
917 // after "make_multisig" but with calls to "exchange_multisig_keys" still needed, and
918 // sets the parameter 'ready' to false to document this particular "in-between" state.
919 // So what may be possible here, with all necessary messages present, is a call to
920 // "exchange_multisig_keys".
921 // Consider only messages belonging to the next round to do, which has the number
922 // "state.multisig_rounds_passed".
923 std::vector<uint32_t> additional_key_set_messages(m_num_authorized_signers, 0);
924
925 for (size_t i = 0; i < m_messages.size(); ++i)
926 {
927 message &m = m_messages[i];
929 && (m.round == state.multisig_rounds_passed))
930 {
931 if (additional_key_set_messages[m.signer_index] == 0)
932 {
933 additional_key_set_messages[m.signer_index] = m.id;
934 }
935 // else duplicate key set, ignore
936 }
937 }
938
939 bool key_sets_complete = message_ids_complete(additional_key_set_messages);
940 if (key_sets_complete)
941 {
942 processing_data data;
944 data.message_ids = additional_key_set_messages;
945 data.message_ids.erase(data.message_ids.begin());
946 data_list.push_back(data);
947 return true;
948 }
949 else
950 {
951 wait_reason = tr("Wallet can't start another key exchange round because key sets from other signers are missing or not complete.");
952 return false;
953 }
954 }
955
956 // Properly exchanging multisig sync data is easiest and most transparent
957 // for the user if a wallet sends its own data first and processes any received
958 // sync data afterwards so that's the order that the MMS enforces here.
959 // (Technically, it seems to work also the other way round.)
960 //
961 // To check whether a NEW round of syncing is necessary the MMS works with a
962 // "wallet state": new state means new syncing needed.
963 //
964 // The MMS monitors the "wallet state" by recording "wallet heights" as
965 // numbers of transfers present in a wallet at the time of message creation. While
966 // not watertight, this quite simple scheme should already suffice to trigger
967 // and orchestrate a sensible exchange of sync data.
968 if (state.has_multisig_partial_key_images || force_sync)
969 {
970 // Sync is necessary and not yet completed: Processing of transactions
971 // will only be possible again once properly synced
972 // Check first whether we generated already OUR sync info; take note of
973 // any processable sync info from other signers on the way in case we need it
974 bool own_sync_data_created = false;
975 std::vector<uint32_t> sync_messages(m_num_authorized_signers, 0);
976 for (size_t i = 0; i < m_messages.size(); ++i)
977 {
978 message &m = m_messages[i];
979 if ((m.type == message_type::multisig_sync_data) && (force_sync || (m.wallet_height == wallet_height)))
980 // It's data for the same "round" of syncing, on the same "wallet height", therefore relevant
981 // With "force_sync" take ANY waiting sync data, maybe it will work out
982 {
984 {
985 own_sync_data_created = true;
986 // Ignore whether sent already or not, and assume as complete if several other signers there
987 }
989 {
990 if (sync_messages[m.signer_index] == 0)
991 {
992 sync_messages[m.signer_index] = m.id;
993 }
994 // else duplicate sync message, ignore
995 }
996 }
997 }
998 if (!own_sync_data_created)
999 {
1000 // As explained above, creating sync data BEFORE processing such data from
1001 // other signers reliably works, so insist on that here
1002 processing_data data;
1004 data_list.push_back(data);
1005 return true;
1006 }
1007 uint32_t id_count = (uint32_t)get_other_signers_id_count(sync_messages);
1008 // Do we have sync data from ALL other signers?
1009 bool all_sync_data = id_count == (m_num_authorized_signers - 1);
1010 // Do we have just ENOUGH sync data to have a minimal viable sync set?
1011 // In cases like 2/3 multisig we don't need messages from ALL other signers, only
1012 // from enough of them i.e. num_required_signers minus 1 messages
1013 bool enough_sync_data = id_count >= (m_num_required_signers - 1);
1014 bool sync = false;
1015 wait_reason = tr("Syncing not done because multisig sync data from other signers are missing or not complete.");
1016 if (all_sync_data)
1017 {
1018 sync = true;
1019 }
1020 else if (enough_sync_data)
1021 {
1022 if (force_sync)
1023 {
1024 sync = true;
1025 }
1026 else
1027 {
1028 // Don't sync, but give a hint how this minimal set COULD be synced if really wanted
1029 wait_reason += (boost::format("\nUse \"mms next sync\" if you want to sync with just %s out of %s authorized signers and transact just with them")
1030 % (m_num_required_signers - 1) % (m_num_authorized_signers - 1)).str();
1031 }
1032 }
1033 if (sync)
1034 {
1035 processing_data data;
1037 for (size_t i = 0; i < sync_messages.size(); ++i)
1038 {
1039 uint32_t id = sync_messages[i];
1040 if (id != 0)
1041 {
1042 data.message_ids.push_back(id);
1043 }
1044 }
1045 data_list.push_back(data);
1046 return true;
1047 }
1048 else
1049 {
1050 // We can't proceed to any transactions until we have synced; "wait_reason" already set above
1051 return false;
1052 }
1053 }
1054
1055 bool waiting_found = false;
1056 bool note_found = false;
1057 bool sync_data_found = false;
1058 for (size_t i = 0; i < m_messages.size(); ++i)
1059 {
1060 message &m = m_messages[i];
1062 {
1063 waiting_found = true;
1064 switch (m.type)
1065 {
1067 {
1068 // We can either submit it ourselves, or send it to any other signer for submission
1069 processing_data data;
1071 data.message_ids.push_back(m.id);
1072 data_list.push_back(data);
1073
1075 for (uint32_t j = 1; j < m_num_authorized_signers; ++j)
1076 {
1077 data.receiving_signer_index = j;
1078 data_list.push_back(data);
1079 }
1080 return true;
1081 }
1082
1084 {
1085 if (m.signer_index == 0)
1086 {
1087 // We started this ourselves, or signed it but with still signatures missing:
1088 // We can send it to any other signer for signing / further signing
1089 // In principle it does not make sense to send it back to somebody who
1090 // already signed, but the MMS does not / not yet keep track of that,
1091 // because that would be somewhat complicated.
1092 processing_data data;
1094 data.message_ids.push_back(m.id);
1095 for (uint32_t j = 1; j < m_num_authorized_signers; ++j)
1096 {
1097 data.receiving_signer_index = j;
1098 data_list.push_back(data);
1099 }
1100 return true;
1101 }
1102 else
1103 {
1104 // Somebody else sent this to us: We can sign it
1105 // It would be possible to just pass it on, but that's not directly supported here
1106 processing_data data;
1108 data.message_ids.push_back(m.id);
1109 data_list.push_back(data);
1110 return true;
1111 }
1112 }
1113
1114 case message_type::note:
1115 note_found = true;
1116 break;
1117
1119 sync_data_found = true;
1120 break;
1121
1122 default:
1123 break;
1124 }
1125 }
1126 }
1127 if (waiting_found)
1128 {
1129 wait_reason = tr("There are waiting messages, but nothing is ready to process under normal circumstances");
1130 if (sync_data_found)
1131 {
1132 wait_reason += tr("\nUse \"mms next sync\" if you want to force processing of the waiting sync data");
1133 }
1134 if (note_found)
1135 {
1136 wait_reason += tr("\nUse \"mms note\" to display the waiting notes");
1137 }
1138 }
1139 else
1140 {
1141 wait_reason = tr("There are no messages waiting to be processed.");
1142 }
1143
1144 return false;
1145}
1146
1148{
1149 for (size_t i = 0; i < data.message_ids.size(); ++i)
1150 {
1152 }
1153}
1154
1156{
1157 message &m = get_message_ref_by_id(id);
1159 {
1160 // So far a fairly cautious and conservative strategy: Only delete from Bitmessage
1161 // when fully processed (and e.g. not already after reception and writing into
1162 // the message store file)
1163 delete_transport_message(id);
1165 }
1166 else if (m.state == message_state::ready_to_send)
1167 {
1169 }
1170 m.modified = (uint64_t)time(NULL);
1171}
1172
1173void message_store::encrypt(crypto::public_key public_key, const std::string &plaintext,
1174 std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv)
1175{
1176 crypto::secret_key encryption_secret_key;
1177 crypto::generate_keys(encryption_public_key, encryption_secret_key);
1178
1179 crypto::key_derivation derivation;
1180 bool success = crypto::generate_key_derivation(public_key, encryption_secret_key, derivation);
1181 THROW_WALLET_EXCEPTION_IF(!success, tools::error::wallet_internal_error, "Failed to generate key derivation for message encryption");
1182
1183 crypto::chacha_key chacha_key;
1184 crypto::generate_chacha_key(&derivation, sizeof(derivation), chacha_key, 1);
1186 ciphertext.resize(plaintext.size());
1187 crypto::chacha20(plaintext.data(), plaintext.size(), chacha_key, iv, &ciphertext[0]);
1188}
1189
1190void message_store::decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv,
1191 const crypto::secret_key &view_secret_key, std::string &plaintext)
1192{
1193 crypto::key_derivation derivation;
1194 bool success = crypto::generate_key_derivation(encryption_public_key, view_secret_key, derivation);
1195 THROW_WALLET_EXCEPTION_IF(!success, tools::error::wallet_internal_error, "Failed to generate key derivation for message decryption");
1196 crypto::chacha_key chacha_key;
1197 crypto::generate_chacha_key(&derivation, sizeof(derivation), chacha_key, 1);
1198 plaintext.resize(ciphertext.size());
1199 crypto::chacha20(ciphertext.data(), ciphertext.size(), chacha_key, iv, &plaintext[0]);
1200}
1201
1203{
1204 message &m = get_message_ref_by_id(id);
1205 const authorized_signer &me = m_signers[0];
1206 const authorized_signer &receiver = m_signers[m.signer_index];
1208 crypto::public_key public_key;
1209
1210 dm.timestamp = (uint64_t)time(NULL);
1211 dm.subject = "MMS V0 " + tools::get_human_readable_timestamp(dm.timestamp);
1212 dm.source_transport_address = me.transport_address;
1213 dm.source_etn_address = me.etn_address;
1215 {
1216 // Encrypt with the public key derived from the auto-config token, and send to the
1217 // transport address likewise derived from that token
1218 public_key = me.auto_config_public_key;
1219 dm.destination_transport_address = me.auto_config_transport_address;
1220 // The destination Electroneum address is not yet known
1221 memset(&dm.destination_etn_address, 0, sizeof(cryptonote::account_public_address));
1222 }
1223 else
1224 {
1225 // Encrypt with the receiver's view public key
1226 public_key = receiver.etn_address.m_view_public_key;
1227 const authorized_signer &receiver = m_signers[m.signer_index];
1228 dm.destination_etn_address = receiver.etn_address;
1229 dm.destination_transport_address = receiver.transport_address;
1230 }
1231 encrypt(public_key, m.content, dm.content, dm.encryption_public_key, dm.iv);
1232 dm.type = (uint32_t)m.type;
1233 dm.hash = crypto::cn_fast_hash(dm.content.data(), dm.content.size());
1234 dm.round = m.round;
1235
1236 crypto::generate_signature(dm.hash, me.etn_address.m_view_public_key, state.view_secret_key, dm.signature);
1237
1238 m_transporter.send_message(dm);
1239
1241 m.sent= (uint64_t)time(NULL);
1242}
1243
1244bool message_store::check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages)
1245{
1246 m_run.store(true, std::memory_order_relaxed);
1247 const authorized_signer &me = m_signers[0];
1248 std::vector<std::string> destinations;
1249 destinations.push_back(me.transport_address);
1250 for (uint32_t i = 1; i < m_num_authorized_signers; ++i)
1251 {
1252 const authorized_signer &m = m_signers[i];
1253 if (m.auto_config_running)
1254 {
1255 destinations.push_back(m.auto_config_transport_address);
1256 }
1257 }
1258 std::vector<transport_message> transport_messages;
1259 bool r = m_transporter.receive_messages(destinations, transport_messages);
1260 if (!m_run.load(std::memory_order_relaxed))
1261 {
1262 // Stop was called, don't waste time processing the messages
1263 // (but once started processing them, don't react to stop request anymore, avoid receiving them "partially)"
1264 return false;
1265 }
1266
1267 bool new_messages = false;
1268 for (size_t i = 0; i < transport_messages.size(); ++i)
1269 {
1270 transport_message &rm = transport_messages[i];
1271 if (any_message_with_hash(rm.hash))
1272 {
1273 // Already seen, do not take again
1274 }
1275 else
1276 {
1277 uint32_t sender_index;
1278 bool take = false;
1279 message_type type = static_cast<message_type>(rm.type);
1280 crypto::secret_key decrypt_key = state.view_secret_key;
1282 {
1283 // Find out which signer sent it by checking which auto config transport address
1284 // the message was sent to
1285 for (uint32_t i = 1; i < m_num_authorized_signers; ++i)
1286 {
1287 const authorized_signer &m = m_signers[i];
1288 if (m.auto_config_transport_address == rm.destination_transport_address)
1289 {
1290 take = true;
1291 sender_index = i;
1292 decrypt_key = m.auto_config_secret_key;
1293 break;
1294 }
1295 }
1296 }
1297 else if (type == message_type::signer_config)
1298 {
1299 // Typically we can't check yet whether we know the sender, so take from any
1300 // and pretend it's from "me" because we might have nothing else yet
1301 take = true;
1302 sender_index = 0;
1303 }
1304 else
1305 {
1306 // Only accept from senders that are known as signer here, otherwise just ignore
1307 take = get_signer_index_by_etn_address(rm.source_etn_address, sender_index);
1308 }
1309 if (take && (type != message_type::auto_config_data))
1310 {
1311 // If the destination address is known, check it as well; this additional filter
1312 // allows using the same transport address for multiple signers
1313 take = rm.destination_etn_address == me.etn_address;
1314 }
1315 if (take)
1316 {
1317 crypto::hash actual_hash = crypto::cn_fast_hash(rm.content.data(), rm.content.size());
1318 THROW_WALLET_EXCEPTION_IF(actual_hash != rm.hash, tools::error::wallet_internal_error, "Message hash mismatch");
1319
1320 bool signature_valid = crypto::check_signature(actual_hash, rm.source_etn_address.m_view_public_key, rm.signature);
1321 THROW_WALLET_EXCEPTION_IF(!signature_valid, tools::error::wallet_internal_error, "Message signature not valid");
1322
1323 std::string plaintext;
1324 decrypt(rm.content, rm.encryption_public_key, rm.iv, decrypt_key, plaintext);
1325 size_t index = add_message(state, sender_index, (message_type)rm.type, message_direction::in, plaintext);
1326 message &m = m_messages[index];
1327 m.hash = rm.hash;
1328 m.transport_id = rm.transport_id;
1329 m.sent = rm.timestamp;
1330 m.round = rm.round;
1331 m.signature_count = rm.signature_count;
1332 messages.push_back(m);
1333 new_messages = true;
1334 }
1335 }
1336 }
1337 return new_messages;
1338}
1339
1340void message_store::delete_transport_message(uint32_t id)
1341{
1342 const message &m = get_message_by_id(id);
1343 if (!m.transport_id.empty())
1344 {
1345 m_transporter.delete_message(m.transport_id);
1346 }
1347}
1348
1349std::string message_store::account_address_to_string(const cryptonote::account_public_address &account_address) const
1350{
1351 return get_account_address_as_str(m_nettype, false, account_address);
1352}
1353
1355{
1356 switch (type)
1357 {
1359 return tr("key set");
1361 return tr("additional key set");
1363 return tr("multisig sync data");
1365 return tr("partially signed tx");
1367 return tr("fully signed tx");
1368 case message_type::note:
1369 return tr("note");
1371 return tr("signer config");
1373 return tr("auto-config data");
1374 default:
1375 return tr("unknown message type");
1376 }
1377}
1378
1380{
1381 switch (direction)
1382 {
1384 return tr("in");
1386 return tr("out");
1387 default:
1388 return tr("unknown message direction");
1389 }
1390}
1391
1393{
1394 switch (state)
1395 {
1397 return tr("ready to send");
1399 return tr("sent");
1401 return tr("waiting");
1403 return tr("processed");
1405 return tr("cancelled");
1406 default:
1407 return tr("unknown message state");
1408 }
1409}
1410
1411// Convert a signer to string suitable for a column in a list, with 'max_width'
1412// Format: label: transport_address
1414{
1415 std::string s = "";
1416 s.reserve(max_width);
1417 uint32_t avail = max_width;
1418 uint32_t label_len = signer.label.length();
1419 if (label_len > avail)
1420 {
1421 s.append(signer.label.substr(0, avail - 2));
1422 s.append("..");
1423 return s;
1424 }
1425 s.append(signer.label);
1426 avail -= label_len;
1427 uint32_t transport_addr_len = signer.transport_address.length();
1428 if ((transport_addr_len > 0) && (avail > 10))
1429 {
1430 s.append(": ");
1431 avail -= 2;
1432 if (transport_addr_len <= avail)
1433 {
1434 s.append(signer.transport_address);
1435 }
1436 else
1437 {
1438 s.append(signer.transport_address.substr(0, avail-2));
1439 s.append("..");
1440 }
1441 }
1442 return s;
1443}
1444
1445}
time_t time
void init(const multisig_wallet_state &state, const std::string &own_label, const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers)
static const char * tr(const char *str)
void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
bool get_signer_index_by_label(const std::string label, uint32_t &index) const
void process_auto_config_data_message(uint32_t id)
void set_active(bool active)
bool check_for_messages(const multisig_wallet_state &state, std::vector< message > &messages)
static const char * message_direction_to_string(message_direction direction)
bool get_processable_messages(const multisig_wallet_state &state, bool force_sync, std::vector< processing_data > &data_list, std::string &wait_reason)
bool get_signer_index_by_etn_address(const cryptonote::account_public_address &etn_address, uint32_t &index) const
void send_message(const multisig_wallet_state &state, uint32_t id)
bool check_auto_config_token(const std::string &raw_token, std::string &adjusted_token) const
size_t add_message(const multisig_wallet_state &state, uint32_t signer_index, message_type type, message_direction direction, const std::string &content)
bool signer_labels_complete() const
size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token)
void set_message_processed_or_sent(uint32_t id)
void get_signer_config(std::string &signer_config)
std::string signer_to_string(const authorized_signer &signer, uint32_t max_width)
void write_to_file(const multisig_wallet_state &state, const std::string &filename)
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content)
void set_options(const boost::program_options::variables_map &vm)
static void init_options(boost::program_options::options_description &desc_params)
void start_auto_config(const multisig_wallet_state &state)
static const char * message_state_to_string(message_state state)
void set_signer(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 > etn_address)
void read_from_file(const multisig_wallet_state &state, const std::string &filename)
bool signer_config_complete() const
const authorized_signer & get_signer(uint32_t index) const
void delete_message(uint32_t id)
void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, std::vector< authorized_signer > &signers)
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const
static const char * message_type_to_string(message_type type)
bool get_message_by_id(uint32_t id, message &m) const
void set_messages_processed(const processing_data &data)
bool delete_message(const std::string &transport_id)
std::string derive_and_receive_transport_address(const std::string &seed)
std::string derive_transport_address(const std::string &seed)
#define tr(x)
const char * key
#define AUTO_CONFIG_TOKEN_BYTES
#define AUTO_CONFIG_TOKEN_PREFIX
#define MERROR(x)
Definition misc_log_ex.h:73
#define MWARNING(x)
Definition misc_log_ex.h:74
#define MINFO(x)
Definition misc_log_ex.h:75
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
const crypto::public_key null_pkey
Definition crypto.cpp:72
void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
Definition crypto.h:292
std::enable_if< std::is_pod< T >::value, T >::type rand()
Definition crypto.h:216
const crypto::secret_key null_skey
Definition crypto.cpp:73
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
void cn_fast_hash(const void *data, size_t length, char *hash)
POD_CLASS key_derivation
Definition crypto.h:101
secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key &recovery_key=secret_key(), bool recover=false)
Definition crypto.h:250
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
Definition crypto.h:272
POD_CLASS public_key
Definition crypto.h:79
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
Definition crypto.h:295
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition crypto.h:262
void hash_to_scalar(const void *data, size_t length, ec_scalar &res)
Definition crypto.cpp:126
POD_CLASS hash
Definition hash.h:50
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
std::string buff_to_hex_nodelimer(const std::string &src)
mdb_size_t count(MDB_cursor *cur)
message_direction
epee::misc_utils::struct_init< transport_message_t > transport_message
message_state
file_error_base< file_save_error_message_index > file_save_error
file_error_base< file_read_error_message_index > file_read_error
std::string get_human_readable_timestamp(uint64_t ts)
Definition util.cpp:1077
const char * buf
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
crypto::secret_key auto_config_secret_key
std::string transport_address
std::string auto_config_token
cryptonote::account_public_address etn_address
crypto::public_key auto_config_public_key
std::string auto_config_transport_address
cryptonote::account_public_address etn_address
std::string transport_address
std::string magic_string
crypto::chacha_iv iv
std::string encrypted_data
uint32_t file_version
uint64_t modified
uint32_t signer_index
uint32_t wallet_height
message_state state
crypto::hash hash
message_type type
std::string content
uint64_t created
std::string transport_id
message_direction direction
uint32_t signature_count
std::vector< uint32_t > message_ids
uint32_t receiving_signer_index
message_processing processing
uint64_t random(const uint64_t max_value)
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define THROW_WALLET_EXCEPTION(err_type,...)