diff options
author | Riccardo Spagni <ric@spagni.net> | 2017-12-17 21:05:16 +0200 |
---|---|---|
committer | Riccardo Spagni <ric@spagni.net> | 2017-12-17 21:05:16 +0200 |
commit | 1cc745113015dda0710e966fceb351e1f50c62e0 (patch) | |
tree | 168da14eda885978e872d247e435f7ea6475d3ee /src/cryptonote_core | |
parent | Merge pull request #2950 (diff) | |
parent | change the N-1/N multisig second message signer for auth (diff) | |
download | monero-1cc745113015dda0710e966fceb351e1f50c62e0.tar.xz |
Merge pull request #2134
ceabc4f9 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)
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 121 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.h | 13 |
3 files changed, 90 insertions, 45 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 169a38f0a..eeed881da 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -59,6 +59,7 @@ target_link_libraries(cryptonote_core common cncrypto blockchain_db + multisig ringct ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 662420bef..89f24a4d4 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -40,12 +40,39 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" +#include "multisig/multisig.h" using namespace crypto; namespace cryptonote { //--------------------------------------------------------------- + void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress) + { + num_stdaddresses = 0; + num_subaddresses = 0; + std::unordered_set<cryptonote::account_public_address> unique_dst_addresses; + for(const tx_destination_entry& dst_entr: destinations) + { + if (change_addr && dst_entr.addr == change_addr) + continue; + if (unique_dst_addresses.count(dst_entr.addr) == 0) + { + unique_dst_addresses.insert(dst_entr.addr); + if (dst_entr.is_subaddress) + { + ++num_subaddresses; + single_dest_subaddress = dst_entr.addr; + } + else + { + ++num_stdaddresses; + } + } + } + LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); + } + //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { tx.vin.clear(); tx.vout.clear(); @@ -161,19 +188,21 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) { std::vector<rct::key> amount_keys; tx.set_null(); amount_keys.clear(); + if (msout) + { + msout->c.clear(); + } tx.version = rct ? 2 : 1; tx.unlock_time = unlock_time; tx.extra = extra; - keypair txkey; - txkey.sec = rct::rct2sk(rct::skGen()); - tx_key = txkey.sec; + crypto::public_key txkey_pub; // if we have a stealth payment id, find it and encrypt it with the tx key now std::vector<tx_extra_field> tx_extra_fields; @@ -193,7 +222,7 @@ namespace cryptonote return false; } - if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) + if (!encrypt_payment_id(payment_id, view_key_pub, tx_key)) { LOG_ERROR("Failed to encrypt payment id"); return false; @@ -247,8 +276,8 @@ namespace cryptonote return false; } - //check that derivated key is equal with real output key - if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) + //check that derivated key is equal with real output key (if non multisig) + if(!msout && !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) { LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" @@ -261,7 +290,7 @@ namespace cryptonote //put key image into tx input txin_to_key input_to_key; input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; + input_to_key.k_image = msout ? rct::rct2ki(src_entr.multisig_kLRki.ki) : img; //fill outputs array and use relative offsets for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) @@ -293,47 +322,29 @@ namespace cryptonote // figure out if we need to make additional tx pubkeys size_t num_stdaddresses = 0; size_t num_subaddresses = 0; - std::unordered_set<cryptonote::account_public_address> unique_dst_addresses; account_public_address single_dest_subaddress; - for(const tx_destination_entry& dst_entr: destinations) - { - if (change_addr && dst_entr.addr == *change_addr) - continue; - if (unique_dst_addresses.count(dst_entr.addr) == 0) - { - unique_dst_addresses.insert(dst_entr.addr); - if (dst_entr.is_subaddress) - { - ++num_subaddresses; - single_dest_subaddress = dst_entr.addr; - } - else - { - ++num_stdaddresses; - } - } - } - LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << "subaddresses"); + classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress); // if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D if (num_stdaddresses == 0 && num_subaddresses == 1) { - txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec))); + txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key))); } else { - txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(txkey.sec))); + txkey_pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(tx_key))); } remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); + add_tx_pub_key_to_extra(tx, txkey_pub); std::vector<crypto::public_key> additional_tx_public_keys; - additional_tx_keys.clear(); // we don't need to include additional tx keys if: // - all the destinations are standard addresses // - there's only one destination which is a subaddress bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1); + if (need_additional_txkeys) + CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys"); uint64_t summary_outs_money = 0; //fill outputs @@ -348,7 +359,7 @@ namespace cryptonote keypair additional_txkey; if (need_additional_txkeys) { - additional_txkey.sec = rct::rct2sk(rct::skGen()); + additional_txkey.sec = additional_tx_keys[output_index]; if (dst_entr.is_subaddress) additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); else @@ -359,20 +370,19 @@ namespace cryptonote if (change_addr && dst_entr.addr == *change_addr) { // sending change to yourself; derivation = a*R - r = crypto::generate_key_derivation(txkey.pub, sender_account_keys.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey.pub << ", " << sender_account_keys.m_view_secret_key << ")"); + r = crypto::generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); } else { // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) - r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec) << ")"); + r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); } if (need_additional_txkeys) { additional_tx_public_keys.push_back(additional_txkey.pub); - additional_tx_keys.push_back(additional_txkey.sec); } if (tx.version > 1) @@ -393,10 +403,11 @@ namespace cryptonote output_index++; summary_outs_money += dst_entr.amount; } + CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys"); remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys)); - LOG_PRINT_L2("tx pubkey: " << txkey.pub); + LOG_PRINT_L2("tx pubkey: " << txkey_pub); if (need_additional_txkeys) { LOG_PRINT_L2("additional tx pubkeys: "); @@ -492,6 +503,7 @@ namespace cryptonote rct::keyV destinations; std::vector<uint64_t> inamounts, outamounts; std::vector<unsigned int> index; + std::vector<rct::multisig_kLRki> kLRki; for (size_t i = 0; i < sources.size(); ++i) { rct::ctkey ctkey; @@ -504,6 +516,10 @@ namespace cryptonote inSk.push_back(ctkey); // inPk: (public key, commitment) // will be done when filling in mixRing + if (msout) + { + kLRki.push_back(sources[i].multisig_kLRki); + } } for (size_t i = 0; i < tx.vout.size(); ++i) { @@ -553,9 +569,9 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, bulletproof); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); @@ -567,13 +583,34 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) + { + keypair txkey = keypair::generate(); + tx_key = txkey.sec; + + // figure out if we need to make additional tx pubkeys + size_t num_stdaddresses = 0; + size_t num_subaddresses = 0; + account_public_address single_dest_subaddress; + classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress); + bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1); + if (need_additional_txkeys) + { + additional_tx_keys.clear(); + for (const auto &d: destinations) + additional_tx_keys.push_back(keypair::generate().sec); + } + + return construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout); + } + //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) { std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index d72f5d13b..5947522e2 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -51,6 +51,7 @@ namespace cryptonote uint64_t amount; //money bool rct; //true if the output is rct rct::key mask; //ringct amount mask + rct::multisig_kLRki multisig_kLRki; //multisig info void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } @@ -63,6 +64,7 @@ namespace cryptonote FIELD(amount) FIELD(rct) FIELD(mask) + FIELD(multisig_kLRki) if (real_output >= outputs.size()) return false; @@ -87,8 +89,9 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false); + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); bool generate_genesis_block( block& bl @@ -98,7 +101,7 @@ namespace cryptonote } -BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) +BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1) BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1) namespace boost @@ -115,6 +118,10 @@ namespace boost a & x.amount; a & x.rct; a & x.mask; + if (ver < 1) + return; + a & x.multisig_kLRki; + a & x.real_out_additional_tx_keys; } template <class Archive> |