Merge pull request #2134
pull/95/headceabc4f9
change the N-1/N multisig second message signer for auth (moneromooo-monero)55c2845d
core_tests: multisig test now tests multiple inputs (moneromooo-monero)98db7ee4
wallet: factor multisig info parsing (moneromooo-monero)31a97e76
wallet: use raw encrypted data in multisig import/export RPC (moneromooo-monero)2fa707d1
wallet: add multisig sign/submit RPC (moneromooo-monero)e36f5b60
Match surae's recommendation to derive multisig keys (moneromooo-monero)a36c261d
wallet2: fix slow multisig unit tests with subaddress patch (moneromooo-monero)fa569712
make multisig work with subaddresses (moneromooo-monero)dffa0dce
simplewallet: add export_raw_multisig command (moneromooo-monero)7f4c220b
simplewallet: add multisig to wallet type in wallet_info output (moneromooo-monero)26529038
wallet: guard against partly initialized multisig wallet (moneromooo-monero)66e34e85
add multisig core test and factor multisig building blocks (moneromooo-monero)f4eda44c
N-1/N multisig (moneromooo-monero)cd64c799
multisig address generation RPC (moneromooo-monero)fff871a4
gen_multisig: generates multisig wallets if participants trust each other (moneromooo-monero)95a21a79
wallet2: allow empty wallet filename to avoid saving data (moneromooo-monero)b84b3565
tests: add multisig unit tests (moneromooo-monero)4c313324
Add N/N multisig tx generation and signing (moneromooo-monero)6d219a92
wallet: add multisig key generation (moneromooo-monero)
commit
1cc7451130
@ -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)
|
@ -0,0 +1,241 @@
|
||||
// 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)
|
||||
{
|
||||
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
|
||||
{
|
||||
tools::fail_msg_writer() << tr("Failed to verify multisig info");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// make the wallets multisig
|
||||
std::vector<std::string> extra_info(total);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
extra_info[n] = wallets[n]->make_multisig(pwd_container->password(), skn, pkn, threshold);
|
||||
ss << " " << name << std::endl;
|
||||
}
|
||||
|
||||
// finalize step if needed
|
||||
if (!extra_info[0].empty())
|
||||
{
|
||||
std::unordered_set<crypto::public_key> pkeys;
|
||||
std::vector<crypto::public_key> signers(total);
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
if (!tools::wallet2::verify_extra_multisig_info(extra_info[n], pkeys, signers[n]))
|
||||
{
|
||||
tools::fail_msg_writer() << genms::tr("Error verifying multisig extra info");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
if (!wallets[n]->finalize_multisig(pwd_container->password(), pkeys, signers))
|
||||
{
|
||||
tools::fail_msg_writer() << genms::tr("Error finalizing multisig");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
# 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(multisig_sources
|
||||
multisig.cpp)
|
||||
|
||||
set(multisig_headers)
|
||||
|
||||
set(multisig_private_headers
|
||||
multisig.h)
|
||||
|
||||
monero_private_headers(multisig
|
||||
${multisig_private_headers})
|
||||
|
||||
monero_add_library(multisig
|
||||
${multisig_sources}
|
||||
${multisig_headers}
|
||||
${multisig_private_headers})
|
||||
|
||||
target_link_libraries(multisig
|
||||
PUBLIC
|
||||
ringct
|
||||
cryptonote_basic
|
||||
common
|
||||
cncrypto
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES})
|
@ -0,0 +1,141 @@
|
||||
// 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.
|
||||
|
||||
#include <unordered_set>
|
||||
#include "include_base_utils.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "multisig.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "multisig"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const rct::key multisig_salt = { {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
|
||||
{
|
||||
rct::keyV data;
|
||||
data.push_back(rct::sk2rct(key));
|
||||
data.push_back(multisig_salt);
|
||||
return rct::rct2sk(rct::hash_to_scalar(data));
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||
{
|
||||
// the multisig spend public key is the sum of all spend public keys
|
||||
multisig_keys.clear();
|
||||
const crypto::secret_key spend_secret_key = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(spend_secret_key, (crypto::public_key&)spend_pkey), "Failed to derive public key");
|
||||
for (const auto &k: spend_keys)
|
||||
rct::addKeys(spend_pkey, spend_pkey, rct::pk2rct(k));
|
||||
multisig_keys.push_back(spend_secret_key);
|
||||
spend_skey = rct::sk2rct(spend_secret_key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||
{
|
||||
multisig_keys.clear();
|
||||
spend_pkey = rct::identity();
|
||||
spend_skey = rct::zero();
|
||||
|
||||
// create all our composite private keys
|
||||
crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||
for (const auto &k: spend_keys)
|
||||
{
|
||||
rct::key sk = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
|
||||
crypto::secret_key msk = get_multisig_blinded_secret_key(rct::rct2sk(sk));
|
||||
multisig_keys.push_back(msk);
|
||||
sc_add(spend_skey.bytes, spend_skey.bytes, (const unsigned char*)msk.data);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
|
||||
{
|
||||
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
|
||||
for (const auto &k: skeys)
|
||||
sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes);
|
||||
return rct::rct2sk(view_skey);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
|
||||
{
|
||||
rct::key spend_public_key = rct::identity();
|
||||
for (const auto &pk: pkeys)
|
||||
{
|
||||
rct::addKeys(spend_public_key, spend_public_key, rct::pk2rct(pk));
|
||||
}
|
||||
return rct::rct2pk(spend_public_key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki)
|
||||
{
|
||||
if (multisig_key_index >= keys.m_multisig_keys.size())
|
||||
return false;
|
||||
crypto::generate_key_image(out_key, keys.m_multisig_keys[multisig_key_index], ki);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R)
|
||||
{
|
||||
rct::scalarmultBase((rct::key&)L, rct::sk2rct(k));
|
||||
crypto::generate_key_image(pkey, k, (crypto::key_image&)R);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki)
|
||||
{
|
||||
cryptonote::keypair in_ephemeral;
|
||||
if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki))
|
||||
return false;
|
||||
std::unordered_set<crypto::key_image> used;
|
||||
for (size_t m = 0; m < keys.m_multisig_keys.size(); ++m)
|
||||
{
|
||||
crypto::key_image pki;
|
||||
bool r = cryptonote::generate_multisig_key_image(keys, m, out_key, pki);
|
||||
if (!r)
|
||||
return false;
|
||||
used.insert(pki);
|
||||
}
|
||||
for (const auto &pki: pkis)
|
||||
{
|
||||
if (used.find(pki) == used.end())
|
||||
{
|
||||
used.insert(pki);
|
||||
rct::addKeys((rct::key&)ki, rct::ki2rct(ki), rct::ki2rct(pki));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
struct account_keys;
|
||||
|
||||
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key);
|
||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
|
||||
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki);
|
||||
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R);
|
||||
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,523 @@
|
||||
// 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
|
||||
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "multisig/multisig.h"
|
||||
#include "common/apply_permutation.h"
|
||||
#include "chaingen.h"
|
||||
#include "multisig.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace crypto;
|
||||
using namespace cryptonote;
|
||||
|
||||
//#define NO_MULTISIG
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Tests
|
||||
|
||||
bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
|
||||
size_t inputs, size_t mixin, uint64_t amount_paid, bool valid,
|
||||
size_t threshold, size_t total, size_t creator, std::vector<size_t> signers,
|
||||
const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
|
||||
const std::function<void(transaction &tx)> &post_tx) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
bool r;
|
||||
|
||||
CHECK_AND_ASSERT_MES(total >= 2, false, "Bad scheme");
|
||||
CHECK_AND_ASSERT_MES(threshold <= total, false, "Bad scheme");
|
||||
CHECK_AND_ASSERT_MES(threshold >= total - 1, false, "Unsupported scheme");
|
||||
#ifdef NO_MULTISIG
|
||||
CHECK_AND_ASSERT_MES(total <= 5, false, "Unsupported scheme");
|
||||
#endif
|
||||
CHECK_AND_ASSERT_MES(inputs >= 1 && inputs <= 8, false, "Inputs should between 1 and 8");
|
||||
|
||||
// given as 1 based for clarity
|
||||
--creator;
|
||||
for (size_t &signer: signers)
|
||||
--signer;
|
||||
|
||||
CHECK_AND_ASSERT_MES(creator < total, false, "invalid creator");
|
||||
for (size_t signer: signers)
|
||||
CHECK_AND_ASSERT_MES(signer < total, false, "invalid signer");
|
||||
|
||||
#ifdef NO_MULTISIG
|
||||
GENERATE_ACCOUNT(acc0);
|
||||
GENERATE_ACCOUNT(acc1);
|
||||
GENERATE_ACCOUNT(acc2);
|
||||
GENERATE_ACCOUNT(acc3);
|
||||
GENERATE_ACCOUNT(acc4);
|
||||
account_base miner_account[5] = {acc0, acc1, acc2, acc3, acc4};
|
||||
#else
|
||||
GENERATE_MULTISIG_ACCOUNT(miner_account, threshold, total);
|
||||
#endif
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account[creator], ts_start);
|
||||
|
||||
// create 8 miner accounts, and have them mine the next 8 blocks
|
||||
// they will have a coinbase with a single out that's pseudo rct
|
||||
constexpr size_t n_coinbases = 8;
|
||||
cryptonote::account_base miner_accounts[n_coinbases];
|
||||
const cryptonote::block *prev_block = &blk_0;
|
||||
cryptonote::block blocks[n_coinbases];
|
||||
for (size_t n = 0; n < n_coinbases; ++n) {
|
||||
// the first block goes to the multisig account
|
||||
miner_accounts[n].generate();
|
||||
account_base &account = n < inputs ? miner_account[creator] : miner_accounts[n];
|
||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, account,
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
|
||||
4, 4, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 1, 4),
|
||||
false, "Failed to generate block");
|
||||
events.push_back(blocks[n]);
|
||||
prev_block = blocks + n;
|
||||
LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
|
||||
LOG_PRINT_L0("in block: " << obj_to_json_str(blocks[n]));
|
||||
}
|
||||
|
||||
// rewind
|
||||
cryptonote::block blk_r, blk_last;
|
||||
{
|
||||
blk_last = blocks[n_coinbases - 1];
|
||||
for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
|
||||
{
|
||||
cryptonote::block blk;
|
||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_accounts[0],
|
||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
|
||||
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 1, 4),
|
||||
false, "Failed to generate block");
|
||||
events.push_back(blk);
|
||||
blk_last = blk;
|
||||
}
|
||||
blk_r = blk_last;
|
||||
}
|
||||
|
||||
cryptonote::keypair in_ephemeral;
|
||||
crypto::public_key tx_pub_key[n_coinbases];
|
||||
crypto::public_key output_pub_key[n_coinbases];
|
||||
for (size_t n = 0; n < n_coinbases; ++n)
|
||||
{
|
||||
tx_pub_key[n] = get_tx_pub_key_from_extra(blocks[n].miner_tx);
|
||||
MDEBUG("tx_pub_key: " << tx_pub_key);
|
||||
output_pub_key[n] = boost::get<txout_to_key>(blocks[n].miner_tx.vout[0].target).key;
|
||||
MDEBUG("output_pub_key: " << output_pub_key);
|
||||
}
|
||||
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[miner_account[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||
|
||||
#ifndef NO_MULTISIG
|
||||
// create k/L/R/ki for that output we're going to spend
|
||||
std::vector<std::vector<std::vector<crypto::secret_key>>> account_k(total);
|
||||
std::vector<std::vector<std::vector<crypto::public_key>>> account_L(total);
|
||||
std::vector<std::vector<std::vector<crypto::public_key>>> account_R(total);
|
||||
std::vector<std::vector<std::vector<crypto::key_image>>> account_ki(total);
|
||||
std::vector<crypto::public_key> additional_tx_keys;
|
||||
for (size_t msidx = 0; msidx < total; ++msidx)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(miner_account[msidx].get_keys().m_account_address.m_spend_public_key == miner_account[0].get_keys().m_account_address.m_spend_public_key,
|
||||
false, "Mismatched spend public keys");
|
||||
|
||||
size_t nlr = threshold < total ? threshold - 1 : 1;
|
||||
account_k[msidx].resize(inputs);
|
||||
account_L[msidx].resize(inputs);
|
||||
account_R[msidx].resize(inputs);
|
||||
account_ki[msidx].resize(inputs);
|
||||
for (size_t tdidx = 0; tdidx < inputs; ++tdidx)
|
||||
{
|
||||
account_L[msidx][tdidx].resize(nlr);
|
||||
account_R[msidx][tdidx].resize(nlr);
|
||||
for (size_t n = 0; n < nlr; ++n)
|
||||
{
|
||||
account_k[msidx][tdidx].push_back(rct::rct2sk(rct::skGen()));
|
||||
cryptonote::generate_multisig_LR(output_pub_key[tdidx], account_k[msidx][tdidx][n], account_L[msidx][tdidx][n], account_R[msidx][tdidx][n]);
|
||||
}
|
||||
size_t numki = miner_account[msidx].get_multisig_keys().size();
|
||||
account_ki[msidx][tdidx].resize(numki);
|
||||
for (size_t kiidx = 0; kiidx < numki; ++kiidx)
|
||||
{
|
||||
r = cryptonote::generate_multisig_key_image(miner_account[msidx].get_keys(), kiidx, output_pub_key[tdidx], account_ki[msidx][tdidx][kiidx]);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate multisig export key image");
|
||||
}
|
||||
MDEBUG("Party " << msidx << ":");
|
||||
MDEBUG("spend: sec " << miner_account[msidx].get_keys().m_spend_secret_key << ", pub " << miner_account[msidx].get_keys().m_account_address.m_spend_public_key);
|
||||
MDEBUG("view: sec " << miner_account[msidx].get_keys().m_view_secret_key << ", pub " << miner_account[msidx].get_keys().m_account_address.m_view_public_key);
|
||||
for (const auto &k: miner_account[msidx].get_multisig_keys())
|
||||
MDEBUG("msk: " << k);
|
||||
for (size_t n = 0; n < account_k[msidx][tdidx].size(); ++n)
|
||||
{
|
||||
MDEBUG("k: " << account_k[msidx][tdidx][n]);
|
||||
MDEBUG("L: " << account_L[msidx][tdidx][n]);
|
||||
MDEBUG("R: " << account_R[msidx][tdidx][n]);
|
||||
}
|
||||
for (const auto &ki: account_ki[msidx][tdidx])
|
||||
MDEBUG("ki: " << ki);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// create kLRki
|
||||
std::vector<rct::multisig_kLRki> kLRkis;
|
||||
std::unordered_set<crypto::public_key> used_L;
|
||||
for (size_t tdidx = 0; tdidx < inputs; ++tdidx)
|
||||
{
|
||||
kLRkis.push_back(rct::multisig_kLRki());
|
||||
rct::multisig_kLRki &kLRki = kLRkis.back();
|
||||
#ifdef NO_MULTISIG
|
||||
kLRki = {rct::zero(), rct::zero(), rct::zero(), rct::zero()};
|
||||
#else
|
||||
kLRki.k = rct::sk2rct(account_k[creator][tdidx][0]);
|
||||
kLRki.L = rct::pk2rct(account_L[creator][tdidx][0]);
|
||||
kLRki.R = rct::pk2rct(account_R[creator][tdidx][0]);
|
||||
MDEBUG("Starting with k " << kLRki.k);
|
||||
MDEBUG("Starting with L " << kLRki.L);
|
||||
MDEBUG("Starting with R " << kLRki.R);
|
||||
for (size_t msidx = 0; msidx < total; ++msidx)
|
||||
{
|
||||
if (msidx == creator)
|
||||
continue;
|
||||
if (std::find(signers.begin(), signers.end(), msidx) == signers.end())
|
||||
continue;
|
||||
for (size_t lr = 0; lr < account_L[msidx][tdidx].size(); ++lr)
|
||||
{
|
||||
if (used_L.find(account_L[msidx][tdidx][lr]) == used_L.end())
|
||||
{
|
||||
used_L.insert(account_L[msidx][tdidx][lr]);
|
||||
MDEBUG("Adding L " << account_L[msidx][tdidx][lr] << " (for k " << account_k[msidx][tdidx][lr] << ")");
|
||||
MDEBUG("Adding R " << account_R[msidx][tdidx][lr]);
|
||||
rct::addKeys((rct::key&)kLRki.L, kLRki.L, rct::pk2rct(account_L[msidx][tdidx][lr]));
|
||||
rct::addKeys((rct::key&)kLRki.R, kLRki.R, rct::pk2rct(account_R[msidx][tdidx][lr]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<crypto::key_image> pkis;
|
||||
for (size_t msidx = 0; msidx < total; ++msidx)
|
||||
for (size_t n = 0; n < account_ki[msidx][tdidx].size(); ++n)
|
||||
pkis.push_back(account_ki[msidx][tdidx][n]);
|
||||
r = cryptonote::generate_multisig_composite_key_image(miner_account[0].get_keys(), subaddresses, output_pub_key[tdidx], tx_pub_key[tdidx], additional_tx_keys, 0, pkis, (crypto::key_image&)kLRki.ki);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate composite key image");
|
||||
MDEBUG("composite ki: " << kLRki.ki);
|
||||
MDEBUG("L: " << kLRki.L);
|
||||
MDEBUG("R: " << kLRki.R);
|
||||
for (size_t n = 1; n < total; ++n)
|
||||
{
|
||||
rct::key ki;
|
||||
r = cryptonote::generate_multisig_composite_key_image(miner_account[n].get_keys(), subaddresses, output_pub_key[tdidx], tx_pub_key[tdidx], additional_tx_keys, 0, pkis, (crypto::key_image&)ki);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate composite key image");
|
||||
CHECK_AND_ASSERT_MES(kLRki.ki == ki, false, "Composite key images do not match");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// create a tx: we have 8 outputs, all from coinbase, so "fake" rct - use 2
|
||||
std::vector<tx_source_entry> sources;
|
||||
for (size_t n = 0; n < inputs; ++n)
|
||||
{
|
||||
sources.resize(sources.size() + 1);
|
||||
tx_source_entry& src = sources.back();
|
||||
|
||||
src.real_output = n;
|
||||
src.amount = blocks[n].miner_tx.vout[0].amount;
|
||||
src.real_out_tx_key = tx_pub_key[n];
|
||||
src.real_output_in_tx_index = 0;
|
||||
src.mask = rct::identity();
|
||||
src.rct = true;
|
||||
src.multisig_kLRki = kLRkis[n];
|
||||
|
||||
for (size_t m = 0; m <= mixin; ++m)
|
||||
{
|
||||
rct::ctkey ctkey;
|
||||
ctkey.dest = rct::pk2rct(boost::get<txout_to_key>(blocks[m].miner_tx.vout[0].target).key);
|
||||
MDEBUG("using " << (m == n ? "real" : "fake") << " input " << ctkey.dest);
|
||||
ctkey.mask = rct::commit(blocks[m].miner_tx.vout[0].amount, rct::identity()); // since those are coinbases, the masks are known
|
||||
src.outputs.push_back(std::make_pair(m, ctkey));
|
||||
}
|
||||
}
|
||||
|
||||
//fill outputs entry
|
||||
tx_destination_entry td;
|
||||
td.addr = miner_account[creator].get_keys().m_account_address;
|
||||
td.amount = amount_paid;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(td);
|
||||
|
||||
if (pre_tx)
|
||||
pre_tx(sources, destinations);
|
||||
|
||||
transaction tx;
|
||||
crypto::secret_key tx_key;
|
||||
#ifdef NO_MULTISIG
|
||||
rct::multisig_out *msoutp = NULL;
|
||||
#else
|
||||
rct::multisig_out msout;
|
||||
rct::multisig_out *msoutp = &msout;
|
||||
#endif
|
||||
std::vector<crypto::secret_key> additional_tx_secret_keys;
|
||||
auto sources_copy = sources;
|
||||
r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, false, msoutp);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||
|
||||
#ifndef NO_MULTISIG
|
||||
// work out the permutation done on sources
|
||||
std::vector<size_t> ins_order;
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
{
|
||||
for (size_t idx = 0; idx < sources_copy.size(); ++idx)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES((size_t)sources_copy[idx].real_output < sources_copy[idx].outputs.size(),
|
||||
false, "Invalid real_output");
|
||||
if (sources_copy[idx].outputs[sources_copy[idx].real_output].second.dest == sources[n].outputs[sources[n].real_output].second.dest)
|
||||
ins_order.push_back(idx);
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(ins_order.size() == sources.size(), false, "Failed to work out sources permutation");
|
||||
#endif
|
||||
|
||||
#ifndef NO_MULTISIG
|
||||
// sign
|
||||
std::unordered_set<crypto::secret_key> used_keys;
|
||||
const std::vector<crypto::secret_key> &msk0 = miner_account[creator].get_multisig_keys();
|
||||
for (const auto &sk: msk0)
|
||||
used_keys.insert(sk);
|
||||
for (size_t signer: signers)
|
||||
{
|
||||
rct::key skey = rct::zero();
|
||||
const std::vector<crypto::secret_key> &msk1 = miner_account[signer].get_multisig_keys();
|
||||
for (size_t n = 0; n < msk1.size(); ++n)
|
||||
{
|
||||
const crypto::secret_key &sk1 = msk1[n];
|
||||
if (used_keys.find(sk1) == used_keys.end())
|
||||
{
|
||||
used_keys.insert(sk1);
|
||||
sc_add(skey.bytes, skey.bytes, rct::sk2rct(sk1).bytes);
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(!(skey == rct::zero()), false, "failed to find secret multisig key to sign transaction");
|
||||
std::vector<unsigned int> indices;
|
||||
for (const auto &src: sources_copy)
|
||||
indices.push_back(src.real_output);
|
||||
rct::keyV k;
|
||||
for (size_t tdidx = 0; tdidx < inputs; ++tdidx)
|
||||
{
|
||||
k.push_back(rct::zero());
|
||||
for (size_t n = 0; n < account_k[signer][tdidx].size(); ++n)
|
||||
{
|
||||
crypto::public_key L;
|
||||
rct::scalarmultBase((rct::key&)L, rct::sk2rct(account_k[signer][tdidx][n]));
|
||||
if (used_L.find(L) != used_L.end())
|
||||
{
|
||||
sc_add(k.back().bytes, k.back().bytes, rct::sk2rct(account_k[signer][tdidx][n]).bytes);
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(!(k.back() == rct::zero()), false, "failed to find k to sign transaction");
|
||||
}
|
||||
tools::apply_permutation(ins_order, indices);
|
||||
tools::apply_permutation(ins_order, k);
|
||||
|
||||
MDEBUG("signing with k size " << k.size());
|
||||
MDEBUG("signing with k " << k.back());
|
||||
MDEBUG("signing with sk " << skey);
|
||||
for (const auto &sk: used_keys)
|
||||
MDEBUG(" created with sk " << sk);
|
||||
MDEBUG("signing with c size " << msout.c.size());
|
||||
MDEBUG("signing with c " << msout.c.back());
|
||||
r = rct::signMultisig(tx.rct_signatures, indices, k, msout, skey);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to sign transaction");
|
||||
}
|
||||
#endif
|
||||
|
||||
// verify this tx is really to the expected address
|
||||
const crypto::public_key tx_pub_key2 = get_tx_pub_key_from_extra(tx, 0);
|
||||
crypto::key_derivation derivation;
|
||||
r = crypto::generate_key_derivation(tx_pub_key2, miner_account[creator].get_keys().m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate derivation");
|
||||
uint64_t n_outs = 0, amount = 0;
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
for (size_t n = 0; n < tx.vout.size(); ++n)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(typeid(txout_to_key) == tx.vout[n].target.type(), false, "Unexpected tx out type");
|
||||
if (is_out_to_acc_precomp(subaddresses, boost::get<txout_to_key>(tx.vout[n].target).key, derivation, additional_derivations, n))
|
||||
{
|
||||
++n_outs;
|
||||
CHECK_AND_ASSERT_MES(tx.vout[n].amount == 0, false, "Destination amount is not zero");
|
||||
rct::key Ctmp;
|
||||
crypto::secret_key scalar1;
|
||||
crypto::derivation_to_scalar(derivation, n, scalar1);
|
||||
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
|
||||
rct::key C = tx.rct_signatures.outPk[n].mask;
|
||||
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
|
||||
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");
|
||||
amount += rct::h2d(ecdh_info.amount);
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(n_outs == 1, false, "Not exactly 1 output was received");
|
||||
CHECK_AND_ASSERT_MES(amount == amount_paid, false, "Amount paid was not the expected amount");
|
||||
|
||||
if (post_tx)
|
||||
post_tx(tx);
|
||||
|
||||
if (!valid)
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(tx);
|
||||
LOG_PRINT_L0("Test tx: " << obj_to_json_str(tx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_22_1_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 2, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_22_1_2_many_inputs::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 4, mixin, amount_paid, true, 2, 2, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_22_2_1::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 2, 2, {1}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_33_1_23::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 3, 3, 1, {2, 3}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_33_3_21::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 3, 3, 3, {2, 1}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_23_1_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 3, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_23_1_3::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 3, 1, {3}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_23_2_1::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 3, 2, {1}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_23_2_3::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 3, 2, {3}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_45_1_234::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 4, 5, 1, {2, 3, 4}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_45_4_135_many_inputs::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 4, mixin, amount_paid, true, 4, 5, 4, {1, 3, 5}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_89_3_1245789::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 8, 9, 3, {1, 2, 4, 5, 7, 8, 9}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_22_1__no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 2, 2, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_33_1__no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 3, 3, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_33_1_2_no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 3, 3, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_33_1_3_no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 3, 3, 1, {3}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_23_1__no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 2, 3, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_45_5_23_no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 4, 5, 5, {2, 3}, NULL, NULL);
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
struct gen_multisig_tx_validation_base : public test_chain_unit_base
|
||||
{
|
||||
gen_multisig_tx_validation_base()
|
||||
: m_invalid_tx_index(0)
|
||||
, m_invalid_block_index(0)
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_multisig_tx_validation_base, mark_invalid_tx);
|
||||
REGISTER_CALLBACK_METHOD(gen_multisig_tx_validation_base, mark_invalid_block);
|
||||
}
|
||||
|
||||
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/)
|
||||
{
|
||||
if (m_invalid_tx_index == event_idx)
|
||||
return tvc.m_verifivation_failed;
|
||||
else
|
||||
return !tvc.m_verifivation_failed && tx_added;
|
||||
}
|
||||
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/)
|
||||
{
|
||||
if (m_invalid_block_index == event_idx)
|
||||
return bvc.m_verifivation_failed;
|
||||
else
|
||||
return !bvc.m_verifivation_failed;
|
||||
}
|
||||
|
||||
bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_block_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_tx_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generate_with(std::vector<test_event_entry>& events, size_t inputs, size_t mixin,
|
||||
uint64_t amount_paid, bool valid,
|
||||
size_t threshold, size_t total, size_t creator, std::vector<size_t> signers,
|
||||
const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
|
||||
const std::function<void(cryptonote::transaction &tx)> &post_tx) const;
|
||||
|
||||
private:
|
||||
size_t m_invalid_tx_index;
|
||||
size_t m_invalid_block_index;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct get_test_options<gen_multisig_tx_validation_base> {
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(4, 1), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options test_options = {
|
||||
hard_forks
|
||||
};
|
||||
};
|
||||
|
||||
// valid
|
||||
struct gen_multisig_tx_valid_22_1_2: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_22_1_2>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_22_1_2_many_inputs: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_22_1_2_many_inputs>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_22_2_1: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_22_2_1>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_33_1_23: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_33_1_23>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_33_3_21: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_33_3_21>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_23_1_2: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_23_1_2>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_23_1_3: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_23_1_3>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_23_2_1: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_23_2_1>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_23_2_3: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_23_2_3>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_45_1_234: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_45_1_234>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_45_4_135_many_inputs: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_45_4_135_many_inputs>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_89_3_1245789: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_89_3_1245789>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
// invalid
|
||||
struct gen_multisig_tx_invalid_22_1__no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_22_1__no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_invalid_33_1__no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_33_1__no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_invalid_33_1_2_no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_33_1_2_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_invalid_33_1_3_no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_33_1_3_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_invalid_23_1__no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_23_1__no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_invalid_45_5_23_no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_45_5_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *address;
|
||||
const char *spendkey;
|
||||
} test_addresses[] =
|
||||
{
|
||||
{
|
||||
"9uvjbU54ZJb8j7Dcq1h3F1DnBRkxXdYUX4pbJ7mE3ghM8uF3fKzqRKRNAKYZXcNLqMg7MxjVVD2wKC2PALUwEveGSC3YSWD",
|
||||
"2dd6e34a234c3e8b5d29a371789e4601e96dee4ea6f7ef79224d1a2d91164c01"
|
||||
},
|
||||
{
|
||||
"9ywDBAyDbb6QKFiZxDJ4hHZqZEQXXCR5EaYNcndUpqPDeE7rEgs6neQdZnhcDrWbURYK8xUjhuG2mVjJdmknrZbcG7NnbaB",
|
||||
"fac47aecc948ce9d3531aa042abb18235b1df632087c55a361b632ffdd6ede0c"
|
||||
},
|
||||
{
|
||||
"9t6Hn946u3eah5cuncH1hB5hGzsTUoevtf4SY7MHN5NgJZh2SFWsyVt3vUhuHyRKyrCQvr71Lfc1AevG3BXE11PQFoXDtD8",
|
||||
"bbd3175ef9fd9f5eefdc43035f882f74ad14c4cf1799d8b6f9001bc197175d02"
|
||||
}
|
||||
};
|
||||
|
||||
static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
||||
{
|
||||
ASSERT_TRUE(idx < sizeof(test_addresses) / sizeof(test_addresses[0]));
|
||||
|
||||
crypto::secret_key spendkey;
|
||||
epee::string_tools::hex_to_pod(test_addresses[idx].spendkey, spendkey);
|
||||
|
||||
try
|
||||
{
|
||||
wallet.init("");
|
||||
wallet.set_subaddress_lookahead(1, 1);
|
||||
wallet.generate("", "", spendkey, true, false);
|
||||
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(true));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MFATAL("Error creating test wallet: " << e.what());
|
||||
ASSERT_TRUE(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, unsigned int M)
|
||||
{
|
||||
ASSERT_TRUE(M <= 2);
|
||||
|
||||
make_wallet(0, wallet0);
|
||||
make_wallet(1, wallet1);
|
||||
|
||||
std::vector<crypto::secret_key> sk0(1), sk1(1);
|
||||
std::vector<crypto::public_key> pk0(1), pk1(1);
|
||||
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
|
||||
ASSERT_FALSE(wallet0.multisig() || wallet1.multisig());
|
||||
wallet0.make_multisig("", sk0, pk0, M);
|
||||
wallet1.make_multisig("", sk1, pk1, M);
|
||||
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(true) == wallet1.get_account().get_public_address_str(true));
|
||||
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 2);
|
||||
ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 2);
|
||||
}
|
||||
|
||||
static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, tools::wallet2 &wallet2, unsigned int M)
|
||||
{
|
||||
ASSERT_TRUE(M <= 3);
|
||||
|
||||
make_wallet(0, wallet0);
|
||||
make_wallet(1, wallet1);
|
||||
make_wallet(2, wallet2);
|
||||
|
||||
std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2);
|
||||
std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2);
|
||||
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
std::string mi2 = wallet2.get_multisig_info();
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk1[1], pk1[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk2[0], pk2[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk2[1], pk2[1]));
|
||||
|
||||
ASSERT_FALSE(wallet0.multisig() || wallet1.multisig() || wallet2.multisig());
|
||||
std::string mxi0 = wallet0.make_multisig("", sk0, pk0, M);
|
||||
std::string mxi1 = wallet1.make_multisig("", sk1, pk1, M);
|
||||
std::string mxi2 = wallet2.make_multisig("", sk2, pk2, M);
|
||||
|
||||
const size_t nset = !mxi0.empty() + !mxi1.empty() + !mxi2.empty();
|
||||
ASSERT_TRUE((M < 3 && nset == 3) || (M == 3 && nset == 0));
|
||||
|
||||
if (nset > 0)
|
||||
{
|
||||
std::unordered_set<crypto::public_key> pkeys;
|
||||
std::vector<crypto::public_key> signers(3, crypto::null_pkey);
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi0, pkeys, signers[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi1, pkeys, signers[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi2, pkeys, signers[2]));
|
||||
ASSERT_TRUE(pkeys.size() == 3);
|
||||
ASSERT_TRUE(wallet0.finalize_multisig("", pkeys, signers));
|
||||
ASSERT_TRUE(wallet1.finalize_multisig("", pkeys, signers));
|
||||
ASSERT_TRUE(wallet2.finalize_multisig("", pkeys, signers));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(true) == wallet1.get_account().get_public_address_str(true));
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(true) == wallet2.get_account().get_public_address_str(true));
|
||||
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
ASSERT_TRUE(wallet2.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_2)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1;
|
||||
make_M_2_wallet(wallet0, wallet1, 2);
|
||||
}
|
||||
|
||||
TEST(multisig, make_3_3)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1, wallet2;
|
||||
make_M_3_wallet(wallet0, wallet1, wallet2, 3);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_3)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1, wallet2;
|
||||
make_M_3_wallet(wallet0, wallet1, wallet2, 2);
|
||||
}
|
Loading…
Reference in new issue