gen_multisig: generates multisig wallets if participants trust each other

This commit is contained in:
moneromooo-monero 2017-06-30 17:36:31 +01:00
parent 95a21a793d
commit fff871a455
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
9 changed files with 283 additions and 10 deletions

View file

@ -129,6 +129,7 @@ endif()
add_subdirectory(cryptonote_protocol) add_subdirectory(cryptonote_protocol)
if(NOT IOS) if(NOT IOS)
add_subdirectory(simplewallet) add_subdirectory(simplewallet)
add_subdirectory(gen_multisig)
add_subdirectory(daemonizer) add_subdirectory(daemonizer)
add_subdirectory(daemon) add_subdirectory(daemon)
add_subdirectory(blockchain_utilities) add_subdirectory(blockchain_utilities)

View file

@ -0,0 +1,54 @@
# Copyright (c) 2017, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(gen_multisig_sources
gen_multisig.cpp)
monero_add_executable(gen_multisig
${gen_multisig_sources})
target_link_libraries(gen_multisig
PRIVATE
wallet
cryptonote_core
cncrypto
common
epee
${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${Readline_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
add_dependencies(gen_multisig
version)
set_property(TARGET gen_multisig
PROPERTY
OUTPUT_NAME "monero-gen-trusted-multisig")
install(TARGETS gen_multisig DESTINATION bin)

View file

@ -0,0 +1,213 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
/*!
* \file gen_multisig.cpp
*
* \brief Generates a set of multisig wallets
*/
#include <iostream>
#include <sstream>
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include "include_base_utils.h"
#include "crypto/crypto.h" // for crypto::secret_key definition
#include "common/i18n.h"
#include "common/command_line.h"
#include "common/util.h"
#include "common/scoped_message_writer.h"
#include "wallet/wallet_args.h"
#include "wallet/wallet2.h"
using namespace std;
using namespace epee;
using namespace cryptonote;
using boost::lexical_cast;
namespace po = boost::program_options;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.gen_multisig"
namespace genms
{
const char* tr(const char* str)
{
return i18n_translate(str, "tools::gen_multisig");
}
}
namespace
{
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)"), ""};
const command_line::arg_descriptor<std::string> arg_scheme = {"scheme", genms::tr("Give threshold and participants at once as M/N"), ""};
const command_line::arg_descriptor<uint32_t> arg_participants = {"participants", genms::tr("How many participants wil share parts of the multisig wallet"), 0};
const command_line::arg_descriptor<uint32_t> arg_threshold = {"threshold", genms::tr("How many signers are required to sign a valid transaction"), 0};
const command_line::arg_descriptor<bool, false> arg_testnet = {"testnet", genms::tr("Create testnet multisig wallets"), false};
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
}
static bool generate_multisig(uint32_t threshold, uint32_t total, const std::string &basename, bool testnet)
{
tools::msg_writer() << (boost::format(genms::tr("Generating %u %u/%u multisig wallets")) % total % threshold % total).str();
const auto pwd_container = tools::password_container::prompt(true, "Enter password for new multisig wallets");
try
{
// create M wallets first
std::vector<boost::shared_ptr<tools::wallet2>> wallets(total);
for (size_t n = 0; n < total; ++n)
{
std::string name = basename + "-" + std::to_string(n + 1);
wallets[n].reset(new tools::wallet2(testnet));
wallets[n]->init("");
wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false);
}
// gather the keys
std::vector<crypto::secret_key> sk(total);
std::vector<crypto::public_key> pk(total);
for (size_t n = 0; n < total; ++n)
{
tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]);
}
// make the wallets multisig
std::stringstream ss;
for (size_t n = 0; n < total; ++n)
{
std::string name = basename + "-" + std::to_string(n + 1);
std::vector<crypto::secret_key> skn;
std::vector<crypto::public_key> pkn;
for (size_t k = 0; k < total; ++k)
{
if (k != n)
{
skn.push_back(sk[k]);
pkn.push_back(pk[k]);
}
}
wallets[n]->make_multisig(pwd_container->password(), skn, pkn, threshold);
ss << " " << name << std::endl;
}
std::string address = wallets[0]->get_account().get_public_address_str(wallets[0]->testnet());
tools::success_msg_writer() << genms::tr("Generated multisig wallets for address ") << address << std::endl << ss.str();
}
catch (const std::exception &e)
{
tools::fail_msg_writer() << genms::tr("Error creating multisig wallets: ") << e.what();
return false;
}
return true;
}
int main(int argc, char* argv[])
{
po::options_description desc_params(wallet_args::tr("Wallet options"));
command_line::add_arg(desc_params, arg_filename_base);
command_line::add_arg(desc_params, arg_scheme);
command_line::add_arg(desc_params, arg_threshold);
command_line::add_arg(desc_params, arg_participants);
command_line::add_arg(desc_params, arg_testnet);
const auto vm = wallet_args::main(
argc, argv,
"monero-gen-multisig [--testnet] [--filename-base=<filename>] [--scheme=M/N] [--threshold=M] [--participants=N]",
genms::tr("This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other"),
desc_params,
boost::program_options::positional_options_description(),
[](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
"monero-gen-multisig.log"
);
if (!vm)
return 1;
bool testnet;
uint32_t threshold = 0, total = 0;
std::string basename;
testnet = command_line::get_arg(*vm, arg_testnet);
if (command_line::has_arg(*vm, arg_scheme))
{
if (sscanf(command_line::get_arg(*vm, arg_scheme).c_str(), "%u/%u", &threshold, &total) != 2)
{
tools::fail_msg_writer() << genms::tr("Error: expected N/M, but got: ") << command_line::get_arg(*vm, arg_scheme);
return 1;
}
}
if (!(*vm)["threshold"].defaulted())
{
if (threshold)
{
tools::fail_msg_writer() << genms::tr("Error: either --scheme or both of --threshold and --participants may be given");
return 1;
}
threshold = command_line::get_arg(*vm, arg_threshold);
}
if (!(*vm)["participants"].defaulted())
{
if (total)
{
tools::fail_msg_writer() << genms::tr("Error: either --scheme or both of --threshold and --participants may be given");
return 1;
}
total = command_line::get_arg(*vm, arg_participants);
}
if (threshold <= 1 || threshold > total)
{
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();
return 1;
}
if (!(*vm)["filename-base"].defaulted() && !command_line::get_arg(*vm, arg_filename_base).empty())
{
basename = command_line::get_arg(*vm, arg_filename_base);
}
else
{
tools::fail_msg_writer() << genms::tr("Error: --filename-base is required");
return 1;
}
if (threshold != total-1 && threshold != total)
{
tools::fail_msg_writer() << genms::tr("Error: unsupported scheme: only N/N and N-1/N are supported");
return 1;
}
if (!generate_multisig(threshold, total, basename, testnet))
return 1;
return 0;
//CATCH_ENTRY_L0("main", 1);
}

View file

@ -6396,6 +6396,7 @@ int main(int argc, char* argv[])
const auto vm = wallet_args::main( const auto vm = wallet_args::main(
argc, argv, argc, argv,
"monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
desc_params, desc_params,
positional_options, positional_options,
[](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },

View file

@ -8108,7 +8108,6 @@ size_t wallet2::import_multisig(std::vector<std::vector<tools::wallet2::multisig
break; break;
} }
MFATAL("import_multisig: updating from import");
for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n) for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
{ {
update_multisig_rescan_info(k, info, n); update_multisig_rescan_info(k, info, n);
@ -8118,7 +8117,6 @@ size_t wallet2::import_multisig(std::vector<std::vector<tools::wallet2::multisig
m_multisig_rescan_info = &info; m_multisig_rescan_info = &info;
try try
{ {
MFATAL("import_multisig: refreshing");
refresh(); refresh();
} }
catch (...) {} catch (...) {}

View file

@ -85,6 +85,7 @@ namespace wallet_args
boost::optional<boost::program_options::variables_map> main( boost::optional<boost::program_options::variables_map> main(
int argc, char** argv, int argc, char** argv,
const char* const usage, const char* const usage,
const char* const notice,
boost::program_options::options_description desc_params, boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options, const boost::program_options::positional_options_description& positional_options,
const std::function<void(const std::string&, bool)> &print, const std::function<void(const std::string&, bool)> &print,
@ -179,6 +180,9 @@ namespace wallet_args
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
} }
if (notice)
Print(print) << notice << ENDL;
if (!command_line::is_arg_defaulted(vm, arg_max_concurrency)) if (!command_line::is_arg_defaulted(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));

View file

@ -48,6 +48,7 @@ namespace wallet_args
boost::optional<boost::program_options::variables_map> main( boost::optional<boost::program_options::variables_map> main(
int argc, char** argv, int argc, char** argv,
const char* const usage, const char* const usage,
const char* const notice,
boost::program_options::options_description desc_params, boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options, const boost::program_options::positional_options_description& positional_options,
const std::function<void(const std::string&, bool)> &print, const std::function<void(const std::string&, bool)> &print,

View file

@ -2361,6 +2361,7 @@ int main(int argc, char** argv) {
const auto vm = wallet_args::main( const auto vm = wallet_args::main(
argc, argv, argc, argv,
"monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]", "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
desc_params, desc_params,
po::positional_options_description(), po::positional_options_description(),
[](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); }, [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); },

View file

@ -85,8 +85,8 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un
std::string mi0 = wallet0.get_multisig_info(); std::string mi0 = wallet0.get_multisig_info();
std::string mi1 = wallet1.get_multisig_info(); std::string mi1 = wallet1.get_multisig_info();
ASSERT_TRUE(wallet0.verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
ASSERT_TRUE(wallet1.verify_multisig_info(mi0, sk1[0], pk1[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
ASSERT_FALSE(wallet0.multisig() || wallet1.multisig()); ASSERT_FALSE(wallet0.multisig() || wallet1.multisig());
wallet0.make_multisig("", sk0, pk0, M); wallet0.make_multisig("", sk0, pk0, M);
@ -118,12 +118,12 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to
std::string mi1 = wallet1.get_multisig_info(); std::string mi1 = wallet1.get_multisig_info();
std::string mi2 = wallet2.get_multisig_info(); std::string mi2 = wallet2.get_multisig_info();
ASSERT_TRUE(wallet0.verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
ASSERT_TRUE(wallet0.verify_multisig_info(mi2, sk0[1], pk0[1])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
ASSERT_TRUE(wallet1.verify_multisig_info(mi0, sk1[0], pk1[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
ASSERT_TRUE(wallet1.verify_multisig_info(mi2, sk1[1], pk1[1])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk1[1], pk1[1]));
ASSERT_TRUE(wallet2.verify_multisig_info(mi0, sk2[0], pk2[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk2[0], pk2[0]));
ASSERT_TRUE(wallet2.verify_multisig_info(mi1, sk2[1], pk2[1])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk2[1], pk2[1]));
// not implemented yet // not implemented yet
if (M < 3) if (M < 3)