aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2017-12-17 21:05:16 +0200
committerRiccardo Spagni <ric@spagni.net>2017-12-17 21:05:16 +0200
commit1cc745113015dda0710e966fceb351e1f50c62e0 (patch)
tree168da14eda885978e872d247e435f7ea6475d3ee /src/cryptonote_core
parentMerge pull request #2950 (diff)
parentchange the N-1/N multisig second message signer for auth (diff)
downloadmonero-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.txt1
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp121
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h13
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>