aboutsummaryrefslogtreecommitdiff
path: root/src/crypto
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/crypto
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 '')
-rw-r--r--src/cryptonote_basic/account.cpp15
-rw-r--r--src/cryptonote_basic/account.h5
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h15
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp53
-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
7 files changed, 174 insertions, 49 deletions
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index fb832d88e..ddc1fc7fc 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -64,6 +64,7 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::forget_spend_key()
{
m_keys.m_spend_secret_key = crypto::secret_key();
+ m_keys.m_multisig_keys.clear();
}
//-----------------------------------------------------------------
crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random)
@@ -123,6 +124,20 @@ DISABLE_VS_WARNINGS(4244 4345)
create_from_keys(address, fake, viewkey);
}
//-----------------------------------------------------------------
+ bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys)
+ {
+ m_keys.m_account_address.m_spend_public_key = spend_public_key;
+ m_keys.m_view_secret_key = view_secret_key;
+ m_keys.m_spend_secret_key = spend_secret_key;
+ m_keys.m_multisig_keys = multisig_keys;
+ return crypto::secret_key_to_public_key(view_secret_key, m_keys.m_account_address.m_view_public_key);
+ }
+ //-----------------------------------------------------------------
+ void account_base::finalize_multisig(const crypto::public_key &spend_public_key)
+ {
+ m_keys.m_account_address.m_spend_public_key = spend_public_key;
+ }
+ //-----------------------------------------------------------------
const account_keys& account_base::get_keys() const
{
return m_keys;
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index e0d5447a2..50af36a9d 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -42,11 +42,13 @@ namespace cryptonote
account_public_address m_account_address;
crypto::secret_key m_spend_secret_key;
crypto::secret_key m_view_secret_key;
+ std::vector<crypto::secret_key> m_multisig_keys;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_account_address)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key)
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
END_KV_SERIALIZE_MAP()
};
@@ -60,6 +62,8 @@ namespace cryptonote
crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
+ bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
+ void finalize_multisig(const crypto::public_key &spend_public_key);
const account_keys& get_keys() const;
std::string get_public_address_str(bool testnet) const;
std::string get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const;
@@ -71,6 +75,7 @@ namespace cryptonote
bool store(const std::string& file_path);
void forget_spend_key();
+ const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; }
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int /*ver*/)
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index 760edf9b9..ed8239176 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -253,6 +253,21 @@ namespace boost
}
template <class Archive>
+ inline void serialize(Archive &a, rct::multisig_kLRki &x, const boost::serialization::version_type ver)
+ {
+ a & x.k;
+ a & x.L;
+ a & x.R;
+ a & x.ki;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::multisig_out &x, const boost::serialization::version_type ver)
+ {
+ a & x.c;
+ }
+
+ template <class Archive>
inline typename std::enable_if<Archive::is_loading::value, void>::type serializeOutPk(Archive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver)
{
rct::keyV outPk;
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index a22c3bdea..8f7ab94db 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -80,6 +80,31 @@ static std::atomic<uint64_t> tx_hashes_cached_count(0);
static std::atomic<uint64_t> block_hashes_calculated_count(0);
static std::atomic<uint64_t> block_hashes_cached_count(0);
+#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}}
+
+namespace cryptonote
+{
+ static inline unsigned char *operator &(ec_point &point) {
+ return &reinterpret_cast<unsigned char &>(point);
+ }
+ static inline const unsigned char *operator &(const ec_point &point) {
+ return &reinterpret_cast<const unsigned char &>(point);
+ }
+
+ // a copy of rct::addKeys, since we can't link to libringct to avoid circular dependencies
+ static void add_public_key(crypto::public_key &AB, const crypto::public_key &A, const crypto::public_key &B) {
+ ge_p3 B2, A2;
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, &B) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, &A) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_cached tmp2;
+ ge_p3_to_cached(&tmp2, &B2);
+ ge_p1p1 tmp3;
+ ge_add(&tmp3, &A2, &tmp2);
+ ge_p1p1_to_p3(&A2, &tmp3);
+ ge_p3_tobytes(&AB, &A2);
+ }
+}
+
namespace cryptonote
{
//---------------------------------------------------------------
@@ -182,6 +207,7 @@ namespace cryptonote
crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b
// step 2: add Hs(a || index_major || index_minor)
+ crypto::secret_key subaddr_sk;
crypto::secret_key scalar_step2;
if (received_index.is_zero())
{
@@ -189,13 +215,32 @@ namespace cryptonote
}
else
{
- crypto::secret_key m = get_subaddress_secret_key(ack.m_view_secret_key, received_index);
- sc_add((unsigned char*)&scalar_step2, (unsigned char*)&scalar_step1, (unsigned char*)&m);
+ subaddr_sk = get_subaddress_secret_key(ack.m_view_secret_key, received_index);
+ sc_add((unsigned char*)&scalar_step2, (unsigned char*)&scalar_step1, (unsigned char*)&subaddr_sk);
}
in_ephemeral.sec = scalar_step2;
- crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub);
- CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, false, "key image helper precomp: given output pubkey doesn't match the derived one");
+
+ if (ack.m_multisig_keys.empty())
+ {
+ // when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
+ CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key");
+ }
+ else
+ {
+ // when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation
+ CHECK_AND_ASSERT_MES(crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key");
+ // and don't forget to add the contribution from the subaddress part
+ if (!received_index.is_zero())
+ {
+ crypto::public_key subaddr_pk;
+ CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(subaddr_sk, subaddr_pk), false, "Failed to derive public key");
+ add_public_key(in_ephemeral.pub, in_ephemeral.pub, subaddr_pk);
+ }
+ }
+
+ CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key,
+ false, "key image helper precomp: given output pubkey doesn't match the derived one");
}
crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
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>