Electroneum
gen_multisig.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2019, 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 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
30 
36 #include <iostream>
37 #include <sstream>
38 #include <boost/program_options.hpp>
39 #include <boost/algorithm/string.hpp>
40 #include <boost/format.hpp>
41 #include "include_base_utils.h"
42 #include "crypto/crypto.h" // for crypto::secret_key definition
43 #include "common/i18n.h"
44 #include "common/command_line.h"
45 #include "common/util.h"
47 #include "wallet/wallet_args.h"
48 #include "wallet/wallet2.h"
49 
50 using namespace std;
51 using namespace epee;
52 using namespace cryptonote;
53 using boost::lexical_cast;
54 namespace po = boost::program_options;
55 
56 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
57 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.gen_multisig"
58 
59 namespace genms
60 {
61  const char* tr(const char* str)
62  {
63  return i18n_translate(str, "tools::gen_multisig");
64  }
65 
66 }
67 
68 namespace
69 {
70  const command_line::arg_descriptor<std::string> arg_filename_base = {"filename-base", genms::tr("Base filename (-1, -2, etc suffixes will be appended as needed)"), ""};
71  const command_line::arg_descriptor<std::string> arg_scheme = {"scheme", genms::tr("Give threshold and participants at once as M/N"), ""};
72  const command_line::arg_descriptor<uint32_t> arg_participants = {"participants", genms::tr("How many participants will share parts of the multisig wallet"), 0};
73  const command_line::arg_descriptor<uint32_t> arg_threshold = {"threshold", genms::tr("How many signers are required to sign a valid transaction"), 0};
74  const command_line::arg_descriptor<bool, false> arg_testnet = {"testnet", genms::tr("Create testnet multisig wallets"), false};
75  const command_line::arg_descriptor<bool, false> arg_stagenet = {"stagenet", genms::tr("Create stagenet multisig wallets"), false};
76  const command_line::arg_descriptor<bool, false> arg_create_address_file = {"create-address-file", genms::tr("Create an address file for new wallets"), false};
77 
79 }
80 
81 static bool generate_multisig(uint32_t threshold, uint32_t total, const std::string &basename, network_type nettype, bool create_address_file)
82 {
83  tools::msg_writer() << (boost::format(genms::tr("Generating %u %u/%u multisig wallets")) % total % threshold % total).str();
84 
85  const auto pwd_container = tools::password_container::prompt(true, "Enter password for new multisig wallets");
86 
87  try
88  {
89  // create M wallets first
90  std::vector<boost::shared_ptr<tools::wallet2>> wallets(total);
91  for (size_t n = 0; n < total; ++n)
92  {
93  std::string name = basename + "-" + std::to_string(n + 1);
94  wallets[n].reset(new tools::wallet2(nettype, 1, false));
95  wallets[n]->init("");
96  wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file);
97  }
98 
99  // gather the keys
100  std::vector<crypto::secret_key> sk(total);
101  std::vector<crypto::public_key> pk(total);
102  for (size_t n = 0; n < total; ++n)
103  {
104  wallets[n]->decrypt_keys(pwd_container->password());
105  if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
106  {
107  tools::fail_msg_writer() << genms::tr("Failed to verify multisig info");
108  return false;
109  }
110  wallets[n]->encrypt_keys(pwd_container->password());
111  }
112 
113  // make the wallets multisig
114  std::vector<std::string> extra_info(total);
115  std::stringstream ss;
116  for (size_t n = 0; n < total; ++n)
117  {
118  std::string name = basename + "-" + std::to_string(n + 1);
119  std::vector<crypto::secret_key> skn;
120  std::vector<crypto::public_key> pkn;
121  for (size_t k = 0; k < total; ++k)
122  {
123  if (k != n)
124  {
125  skn.push_back(sk[k]);
126  pkn.push_back(pk[k]);
127  }
128  }
129  extra_info[n] = wallets[n]->make_multisig(pwd_container->password(), skn, pkn, threshold);
130  ss << " " << name << std::endl;
131  }
132 
133  //exchange keys unless exchange_multisig_keys returns no extra info
134  while (!extra_info[0].empty())
135  {
136  std::unordered_set<crypto::public_key> pkeys;
137  std::vector<crypto::public_key> signers(total);
138  for (size_t n = 0; n < total; ++n)
139  {
140  if (!tools::wallet2::verify_extra_multisig_info(extra_info[n], pkeys, signers[n]))
141  {
142  tools::fail_msg_writer() << genms::tr("Error verifying multisig extra info");
143  return false;
144  }
145  }
146  for (size_t n = 0; n < total; ++n)
147  {
148  extra_info[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), pkeys, signers);
149  }
150  }
151 
152  std::string address = wallets[0]->get_account().get_public_address_str(wallets[0]->nettype());
153  tools::success_msg_writer() << genms::tr("Generated multisig wallets for address ") << address << std::endl << ss.str();
154  }
155  catch (const std::exception &e)
156  {
157  tools::fail_msg_writer() << genms::tr("Error creating multisig wallets: ") << e.what();
158  return false;
159  }
160 
161  return true;
162 }
163 
164 int main(int argc, char* argv[])
165 {
166  TRY_ENTRY();
167 
168  po::options_description desc_params(wallet_args::tr("Wallet options"));
169  command_line::add_arg(desc_params, arg_filename_base);
170  command_line::add_arg(desc_params, arg_scheme);
171  command_line::add_arg(desc_params, arg_threshold);
172  command_line::add_arg(desc_params, arg_participants);
173  command_line::add_arg(desc_params, arg_testnet);
174  command_line::add_arg(desc_params, arg_stagenet);
175  command_line::add_arg(desc_params, arg_create_address_file);
176 
177  boost::optional<po::variables_map> vm;
178  bool should_terminate = false;
179  std::tie(vm, should_terminate) = wallet_args::main(
180  argc, argv,
181  "electroneum-gen-multisig [(--testnet|--stagenet)] [--filename-base=<filename>] [--scheme=M/N] [--threshold=M] [--participants=N]",
182  genms::tr("This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other"),
183  desc_params,
184  boost::program_options::positional_options_description(),
185  [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
186  "electroneum-gen-multisig.log"
187  );
188  if (!vm)
189  return 1;
190  if (should_terminate)
191  return 0;
192 
193  bool testnet, stagenet;
194  uint32_t threshold = 0, total = 0;
195  std::string basename;
196 
197  testnet = command_line::get_arg(*vm, arg_testnet);
198  stagenet = command_line::get_arg(*vm, arg_stagenet);
199  if (testnet && stagenet)
200  {
201  tools::fail_msg_writer() << genms::tr("Error: Can't specify more than one of --testnet and --stagenet");
202  return 1;
203  }
204  if (command_line::has_arg(*vm, arg_scheme))
205  {
206  if (sscanf(command_line::get_arg(*vm, arg_scheme).c_str(), "%u/%u", &threshold, &total) != 2)
207  {
208  tools::fail_msg_writer() << genms::tr("Error: expected N/M, but got: ") << command_line::get_arg(*vm, arg_scheme);
209  return 1;
210  }
211  }
212  if (!(*vm)["threshold"].defaulted())
213  {
214  if (threshold)
215  {
216  tools::fail_msg_writer() << genms::tr("Error: either --scheme or both of --threshold and --participants may be given");
217  return 1;
218  }
219  threshold = command_line::get_arg(*vm, arg_threshold);
220  }
221  if (!(*vm)["participants"].defaulted())
222  {
223  if (total)
224  {
225  tools::fail_msg_writer() << genms::tr("Error: either --scheme or both of --threshold and --participants may be given");
226  return 1;
227  }
228  total = command_line::get_arg(*vm, arg_participants);
229  }
230  if (threshold <= 1 || threshold > total)
231  {
232  tools::fail_msg_writer() << (boost::format(genms::tr("Error: expected N > 1 and N <= M, but got N==%u and M==%d")) % threshold % total).str();
233  return 1;
234  }
235  if (!(*vm)["filename-base"].defaulted() && !command_line::get_arg(*vm, arg_filename_base).empty())
236  {
237  basename = command_line::get_arg(*vm, arg_filename_base);
238  }
239  else
240  {
241  tools::fail_msg_writer() << genms::tr("Error: --filename-base is required");
242  return 1;
243  }
244 
245  bool create_address_file = command_line::get_arg(*vm, arg_create_address_file);
246  if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : stagenet ? STAGENET : MAINNET, create_address_file))
247  return 1;
248 
249  return 0;
250  CATCH_ENTRY_L0("main", 1);
251 }
#define tr(x)
Definition: common_defines.h:4
const char * tr(const char *str)
const command_line::arg_descriptor< std::vector< std::string > > arg_command
::std::string string
Definition: gtest-port.h:1097
STL namespace.
const char * tr(const char *str)
Definition: wallet_args.cpp:81
const char * name
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:323
static bool verify_extra_multisig_info(const std::string &data, std::unordered_set< crypto::public_key > &pkeys, crypto::public_key &signer)
Definition: wallet2.cpp:5586
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition: password.cpp:253
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
std::enable_if<!std::is_same< T, bool >::value, bool >::type has_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:258
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
unsigned int uint32_t
Definition: stdint.h:126
void skGen(key &sk)
Definition: rctOps.cpp:253
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
scoped_message_writer success_msg_writer(bool color=true)
std::pair< boost::optional< boost::program_options::variables_map >, bool > main(int argc, char **argv, const char *const usage, const char *const notice, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description &positional_options, const std::function< void(const std::string &, bool)> &print, const char *default_log_name, bool log_to_console)
Definition: wallet_args.cpp:86
scoped_message_writer fail_msg_writer()
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
const char * address
Definition: multisig.cpp:37
std::string to_string(t_connection_type type)
int main(int argc, char *argv[])
scoped_message_writer msg_writer(epee::console_colors color=epee::console_color_default)
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
static bool verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey)
Definition: wallet2.cpp:5548
uint8_t threshold
Definition: blockchain.cpp:92