Skip to content

Commit

Permalink
Arbitrary M/N multisig schemes:
Browse files Browse the repository at this point in the history
* support in wallet2
* support in monero-wallet-cli
* support in monero-wallet-rpc
* support in wallet api
* support in monero-gen-trusted-multisig
* unit tests for multisig wallets creation
  • Loading branch information
naughtyfox committed Oct 1, 2018
1 parent 8bf5a00 commit 9f3963e
Show file tree
Hide file tree
Showing 15 changed files with 625 additions and 219 deletions.
15 changes: 3 additions & 12 deletions src/gen_multisig/gen_multisig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
ss << " " << name << std::endl;
}

// finalize step if needed
if (!extra_info[0].empty())
//exchange keys unless exchange_multisig_keys returns no extra info
while (!extra_info[0].empty())
{
std::unordered_set<crypto::public_key> pkeys;
std::vector<crypto::public_key> signers(total);
Expand All @@ -145,11 +145,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
}
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;
}
extra_info[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), pkeys, signers);
}
}

Expand Down Expand Up @@ -244,11 +240,6 @@ int main(int argc, char* argv[])
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;
}
bool create_address_file = command_line::get_arg(*vm, arg_create_address_file);
if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : stagenet ? STAGENET : MAINNET, create_address_file))
return 1;
Expand Down
44 changes: 43 additions & 1 deletion src/multisig/multisig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,43 @@ namespace cryptonote
}
}
//-----------------------------------------------------------------
std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations)
{
std::vector<crypto::public_key> multisig_keys;
crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
for (const auto &k: derivations)
{
rct::key d = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
multisig_keys.push_back(rct::rct2pk(d));
}

return multisig_keys;
}
//-----------------------------------------------------------------
crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& multisig_keys)
{
rct::key secret_key = rct::zero();
for (const auto &k: multisig_keys)
{
sc_add(secret_key.bytes, secret_key.bytes, (const unsigned char*)k.data);
}

return rct::rct2sk(secret_key);
}
//-----------------------------------------------------------------
std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations)
{
std::vector<crypto::secret_key> multisig_keys;
multisig_keys.reserve(derivations.size());

for (const auto &k: derivations)
{
multisig_keys.emplace_back(get_multisig_blinded_secret_key(rct::rct2sk(rct::pk2rct(k))));
}

return multisig_keys;
}
//-----------------------------------------------------------------
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));
Expand All @@ -92,7 +129,7 @@ namespace cryptonote
return rct::rct2sk(view_skey);
}
//-----------------------------------------------------------------
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
{
rct::key spend_public_key = rct::identity();
for (const auto &pk: pkeys)
Expand Down Expand Up @@ -141,4 +178,9 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
{
CHECK_AND_ASSERT_THROW_MES(participants >= threshold, "participants must be greater or equal than threshold");
return participants - threshold + 1;
}
}
24 changes: 23 additions & 1 deletion src/multisig/multisig.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,31 @@ namespace cryptonote
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);
/**
* @brief generate_multisig_derivations performs common DH key derivation.
* Each middle round in M/N scheme is DH exchange of public multisig keys of other participants multiplied by secret spend key of current participant.
* this functions does the following: new multisig key = secret spend * public multisig key
* @param keys - current wallet's keys
* @param derivations - public multisig keys of other participants
* @return new public multisig keys derived from previous round. This data needs to be exchange with other participants
*/
std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations);
crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& derivations);
/**
* @brief calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: mi = H(Mi)
* @param derivations - others' participants public multisig keys.
* @return vector of current wallet's multisig secret keys
*/
std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations);
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);
/**
* @brief generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all of public multisig keys
* @param pkeys unique public multisig keys
* @return multisig wallet's spend public key
*/
crypto::public_key generate_multisig_M_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);
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold);
}
61 changes: 60 additions & 1 deletion src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
{
success_msg_writer() << tr("Another step is needed");
success_msg_writer() << multisig_extra_info;
success_msg_writer() << tr("Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info");
success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
return true;
}
}
Expand Down Expand Up @@ -1042,6 +1042,61 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
return true;
}

bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) {
bool ready;
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
if (!m_wallet->multisig(&ready))
{
fail_msg_writer() << tr("This wallet is not multisig");
return true;
}
if (ready)
{
fail_msg_writer() << tr("This wallet is already finalized");
return true;
}

const auto orig_pwd_container = get_and_verify_password();
if(orig_pwd_container == boost::none)
{
fail_msg_writer() << tr("Your original password was incorrect.");
return true;
}

if (args.size() < 2)
{
fail_msg_writer() << tr("usage: exchange_multisig_keys <multisiginfo1> [<multisiginfo2>...]");
return true;
}

try
{
std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
if (!multisig_extra_info.empty())
{
message_writer() << tr("Another step is needed");
message_writer() << multisig_extra_info;
message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
return true;
} else {
uint32_t threshold, total;
m_wallet->multisig(NULL, &threshold, &total);
success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total;
}
}
catch (const std::exception &e)
{
fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what();
return true;
}

return true;
}

bool simple_wallet::export_multisig(const std::vector<std::string> &args)
{
bool ready;
Expand Down Expand Up @@ -2552,6 +2607,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::finalize_multisig, this, _1),
tr("finalize_multisig <string> [<string>...]"),
tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets"));
m_cmd_binder.set_handler("exchange_multisig_keys",
boost::bind(&simple_wallet::exchange_multisig_keys, this, _1),
tr("exchange_multisig_keys <string> [<string>...]"),
tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets"));
m_cmd_binder.set_handler("export_multisig_info",
boost::bind(&simple_wallet::export_multisig, this, _1),
tr("export_multisig_info <filename>"),
Expand Down
1 change: 1 addition & 0 deletions src/simplewallet/simplewallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ namespace cryptonote
bool prepare_multisig(const std::vector<std::string>& args);
bool make_multisig(const std::vector<std::string>& args);
bool finalize_multisig(const std::vector<std::string> &args);
bool exchange_multisig_keys(const std::vector<std::string> &args);
bool export_multisig(const std::vector<std::string>& args);
bool import_multisig(const std::vector<std::string>& args);
bool accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs);
Expand Down
14 changes: 14 additions & 0 deletions src/wallet/api/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,20 @@ string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold)
return string();
}

std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info) {
try {
clearStatus();
checkMultisigWalletNotReady(m_wallet);

return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info);
} catch (const exception& e) {
LOG_ERROR("Error on exchanging multisig keys: ") << e.what();
setStatusError(string(tr("Failed to make multisig: ")) + e.what());
}

return string();
}

bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) {
try {
clearStatus();
Expand Down
1 change: 1 addition & 0 deletions src/wallet/api/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class WalletImpl : public Wallet
MultisigState multisig() const override;
std::string getMultisigInfo() const override;
std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
std::string exchangeMultisigKeys(const std::vector<std::string> &info) override;
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
Expand Down
6 changes: 6 additions & 0 deletions src/wallet/api/wallet2_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,12 @@ struct Wallet
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
*/
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
/**
* @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N)
* @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
* @return new info string if more rounds required or an empty string if wallet creation is done
*/
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info) = 0;
/**
* @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
* @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
Expand Down
Loading

0 comments on commit 9f3963e

Please sign in to comment.