diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blocks/checkpoints.dat | bin | 272772 -> 332676 bytes | |||
-rw-r--r-- | src/checkpoints/checkpoints.cpp | 1 | ||||
-rw-r--r-- | src/cryptonote_config.h | 2 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 2 | ||||
-rw-r--r-- | src/hardforks/hardforks.cpp | 6 | ||||
-rw-r--r-- | src/multisig/multisig_tx_builder_ringct.cpp | 142 | ||||
-rw-r--r-- | src/multisig/multisig_tx_builder_ringct.h | 1 | ||||
-rw-r--r-- | src/version.cpp.in | 4 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 27 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 15 |
10 files changed, 171 insertions, 29 deletions
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex e75e379f2..2ed1d630f 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 27e77cae8..330e3653c 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -239,6 +239,7 @@ namespace cryptonote ADD_CHECKPOINT2(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314", "0x9cb8b6ff2978c6"); ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb"); ADD_CHECKPOINT2(2182500, "0d22b5f81982eff21d094af9e821dc2007e6342069e3b1a37b15d97646353124", "0xead4a874083492"); + ADD_CHECKPOINT2(2661600, "41c9060e8426012238e8a26da26fcb90797436896cc70886a894c2c560bcccf2", "0x2e0d87526ff161f"); return true; } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 962346017..2ec194ef8 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -246,6 +246,8 @@ namespace config const unsigned char HASH_KEY_CLSAG_AGG_1[] = "CLSAG_agg_1"; const char HASH_KEY_MESSAGE_SIGNING[] = "MoneroMessageSignature"; const unsigned char HASH_KEY_MM_SLOT = 'm'; + const constexpr char HASH_KEY_MULTISIG_TX_PRIVKEYS_SEED[] = "multisig_tx_privkeys_seed"; + const constexpr char HASH_KEY_MULTISIG_TX_PRIVKEYS[] = "multisig_tx_privkeys"; // Multisig const uint32_t MULTISIG_MAX_SIGNERS{16}; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 5b7b4353d..c37dfe9e7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -5604,7 +5604,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "8da80ca560793f252d1d4ed449c85d75c74867f3f86b8832c8e3f88b1cbb6ae3"; +static const char expected_block_hashes_hash[] = "e9371004b9f6be59921b27bc81e28b4715845ade1c6d16891d5c455f72e21365"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp index 0dd0cf5f0..336ef6519 100644 --- a/src/hardforks/hardforks.cpp +++ b/src/hardforks/hardforks.cpp @@ -71,8 +71,8 @@ const hardfork_t mainnet_hard_forks[] = { { 13, 2210000, 0, 1598180817 }, { 14, 2210720, 0, 1598180818 }, - { 15, 8000000, 0, 1608223241 }, // temp so tests test with these consensus rules - { 16, 8000001, 0, 1608223242 }, // temp so tests test with these consensus rules + { 15, 2688888, 0, 1656629117 }, + { 16, 2689608, 0, 1656629118 }, }; const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); const uint64_t mainnet_hard_fork_version_1_till = 1009826; @@ -122,5 +122,7 @@ const hardfork_t stagenet_hard_forks[] = { { 12, 454721, 0, 1571419280 }, { 13, 675405, 0, 1598180817 }, { 14, 676125, 0, 1598180818 }, + { 15, 1151000, 0, 1656629117 }, + { 16, 1151720, 0, 1656629118 }, }; const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]); diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp index cbc556b71..e5c9ac483 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -34,6 +34,7 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_config.h" #include "cryptonote_core/cryptonote_tx_utils.h" #include "device/device.hpp" #include "multisig_clsag_context.h" @@ -47,6 +48,7 @@ #include <cstring> #include <limits> #include <set> +#include <string> #include <unordered_map> #include <unordered_set> #include <vector> @@ -242,6 +244,80 @@ static bool set_tx_extra( } //---------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------- +static void make_tx_secret_key_seed(const crypto::secret_key& tx_secret_key_entropy, + const std::vector<cryptonote::tx_source_entry>& sources, + crypto::secret_key& tx_secret_key_seed) +{ + // seed = H(H("domain separator"), entropy, {KI}) + static const std::string domain_separator{config::HASH_KEY_MULTISIG_TX_PRIVKEYS_SEED}; + + rct::keyV hash_context; + hash_context.reserve(2 + sources.size()); + auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{ + memwipe(hash_context.data(), hash_context.size()); + }); + hash_context.emplace_back(); + rct::cn_fast_hash(hash_context.back(), domain_separator.data(), domain_separator.size()); //domain sep + hash_context.emplace_back(rct::sk2rct(tx_secret_key_entropy)); //entropy + + for (const cryptonote::tx_source_entry& source : sources) + hash_context.emplace_back(source.multisig_kLRki.ki); //{KI} + + // set the seed + tx_secret_key_seed = rct::rct2sk(rct::cn_fast_hash(hash_context)); +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +static void make_tx_secret_keys(const crypto::secret_key& tx_secret_key_seed, + const std::size_t num_tx_keys, + std::vector<crypto::secret_key>& tx_secret_keys) +{ + // make tx secret keys as a hash chain of the seed + // h1 = H_n(seed || H("domain separator")) + // h2 = H_n(seed || h1) + // h3 = H_n(seed || h2) + // ... + static const std::string domain_separator{config::HASH_KEY_MULTISIG_TX_PRIVKEYS}; + + rct::keyV hash_context; + hash_context.resize(2); + auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{ + memwipe(hash_context.data(), hash_context.size()); + }); + hash_context[0] = rct::sk2rct(tx_secret_key_seed); + rct::cn_fast_hash(hash_context[1], domain_separator.data(), domain_separator.size()); + + tx_secret_keys.clear(); + tx_secret_keys.resize(num_tx_keys); + + for (crypto::secret_key& tx_secret_key : tx_secret_keys) + { + // advance the hash chain + hash_context[1] = rct::hash_to_scalar(hash_context); + + // set this key + tx_secret_key = rct::rct2sk(hash_context[1]); + } +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +static bool collect_tx_secret_keys(const std::vector<crypto::secret_key>& tx_secret_keys, + crypto::secret_key& tx_secret_key, + std::vector<crypto::secret_key>& tx_aux_secret_keys) +{ + if (tx_secret_keys.size() == 0) + return false; + + tx_secret_key = tx_secret_keys[0]; + tx_aux_secret_keys.clear(); + tx_aux_secret_keys.reserve(tx_secret_keys.size() - 1); + for (std::size_t tx_key_index{1}; tx_key_index < tx_secret_keys.size(); ++tx_key_index) + tx_aux_secret_keys.emplace_back(tx_secret_keys[tx_key_index]); + + return true; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- static bool compute_keys_for_destinations( const cryptonote::account_keys& account_keys, const std::uint32_t subaddr_account, @@ -250,6 +326,7 @@ static bool compute_keys_for_destinations( const std::vector<std::uint8_t>& extra, const bool use_view_tags, const bool reconstruction, + const crypto::secret_key& tx_secret_key_seed, crypto::secret_key& tx_secret_key, std::vector<crypto::secret_key>& tx_aux_secret_keys, rct::keyV& output_public_keys, @@ -288,8 +365,35 @@ static bool compute_keys_for_destinations( unique_std_recipients.insert(dst_entr.addr); } - if (not reconstruction) { - tx_secret_key = rct::rct2sk(rct::skGen()); + // figure out how many tx secret keys are needed + // - tx aux keys: add if there are > 1 non-change recipients, with at least one to a subaddress + const std::size_t num_destinations = destinations.size(); + const bool need_tx_aux_keys = unique_subbaddr_recipients.size() + bool(unique_std_recipients.size()) > 1; + + const std::size_t num_tx_keys = 1 + (need_tx_aux_keys ? num_destinations : 0); + + // make tx secret keys + std::vector<crypto::secret_key> all_tx_secret_keys; + make_tx_secret_keys(tx_secret_key_seed, num_tx_keys, all_tx_secret_keys); + + // split up tx secret keys + crypto::secret_key tx_secret_key_temp; + std::vector<crypto::secret_key> tx_aux_secret_keys_temp; + if (not collect_tx_secret_keys(all_tx_secret_keys, tx_secret_key_temp, tx_aux_secret_keys_temp)) + return false; + + if (reconstruction) + { + // when reconstructing, the tx secret keys should be reproducible from input seed + if (!(tx_secret_key == tx_secret_key_temp)) + return false; + if (!(tx_aux_secret_keys == tx_aux_secret_keys_temp)) + return false; + } + else + { + tx_secret_key = tx_secret_key_temp; + tx_aux_secret_keys = std::move(tx_aux_secret_keys_temp); } // tx pub key: R @@ -312,17 +416,6 @@ static bool compute_keys_for_destinations( } // additional tx pubkeys: R_t - // - add if there are > 1 non-change recipients, with at least one to a subaddress - const std::size_t num_destinations = destinations.size(); - - const bool need_tx_aux_keys = unique_subbaddr_recipients.size() + bool(unique_std_recipients.size()) > 1; - if (not reconstruction and need_tx_aux_keys) { - tx_aux_secret_keys.clear(); - tx_aux_secret_keys.reserve(num_destinations); - for(std::size_t i = 0; i < num_destinations; ++i) - tx_aux_secret_keys.push_back(rct::rct2sk(rct::skGen())); - } - output_public_keys.resize(num_destinations); view_tags.resize(num_destinations); std::vector<crypto::public_key> tx_aux_public_keys; @@ -738,6 +831,7 @@ bool tx_builder_ringct_t::init( const bool reconstruction, crypto::secret_key& tx_secret_key, std::vector<crypto::secret_key>& tx_aux_secret_keys, + crypto::secret_key& tx_secret_key_entropy, cryptonote::transaction& unsigned_tx ) { @@ -765,6 +859,23 @@ bool tx_builder_ringct_t::init( // sort inputs sort_sources(sources); + // prepare tx secret key seed (must be AFTER sorting sources) + // - deriving the seed from sources plus entropy ensures uniqueness for every new tx attempt + // - the goal is that two multisig txs added to the chain will never have outputs with the same onetime addresses, + // which would burn funds (embedding the inputs' key images guarantees this) + // - it is acceptable if two tx attempts use the same input set and entropy (only a malicious tx proposer will do + // that, but all it can accomplish is leaking information about the recipients - which a malicious proposer can + // easily do outside the signing ritual anyway) + if (not reconstruction) + tx_secret_key_entropy = rct::rct2sk(rct::skGen()); + + // expect not null (note: wallet serialization code may set this to null if handling an old partial tx) + if (tx_secret_key_entropy == crypto::null_skey) + return false; + + crypto::secret_key tx_secret_key_seed; + make_tx_secret_key_seed(tx_secret_key_entropy, sources, tx_secret_key_seed); + // get secret keys for signing input CLSAGs (multisig: or for the initial partial signature) rct::keyV input_secret_keys; auto input_secret_keys_wiper = epee::misc_utils::create_scope_leave_handler([&]{ @@ -791,6 +902,7 @@ bool tx_builder_ringct_t::init( extra, use_view_tags, reconstruction, + tx_secret_key_seed, tx_secret_key, tx_aux_secret_keys, output_public_keys, @@ -921,6 +1033,7 @@ bool tx_builder_ringct_t::finalize_tx( cryptonote::transaction& unsigned_tx ) { + // checks const std::size_t num_sources = sources.size(); if (num_sources != unsigned_tx.rct_signatures.p.CLSAGs.size()) return false; @@ -928,6 +1041,8 @@ bool tx_builder_ringct_t::finalize_tx( return false; if (num_sources != s.size()) return false; + + // finalize tx signatures for (std::size_t i = 0; i < num_sources; ++i) { const std::size_t ring_size = unsigned_tx.rct_signatures.p.CLSAGs[i].s.size(); if (sources[i].real_output >= ring_size) @@ -935,6 +1050,7 @@ bool tx_builder_ringct_t::finalize_tx( unsigned_tx.rct_signatures.p.CLSAGs[i].s[sources[i].real_output] = s[i]; unsigned_tx.rct_signatures.p.CLSAGs[i].c1 = c_0[i]; } + return true; } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/multisig/multisig_tx_builder_ringct.h b/src/multisig/multisig_tx_builder_ringct.h index 67ef9e065..853934659 100644 --- a/src/multisig/multisig_tx_builder_ringct.h +++ b/src/multisig/multisig_tx_builder_ringct.h @@ -82,6 +82,7 @@ public: const bool reconstruction, crypto::secret_key& tx_secret_key, std::vector<crypto::secret_key>& tx_aux_secret_keys, + crypto::secret_key& tx_secret_key_entropy, cryptonote::transaction& unsigned_tx ); diff --git a/src/version.cpp.in b/src/version.cpp.in index 9f6ffd97b..c6d473bf9 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.17.0.0" -#define DEF_MONERO_RELEASE_NAME "Oxygen Orion" +#define DEF_MONERO_VERSION "0.18.0.0" +#define DEF_MONERO_RELEASE_NAME "Fluorine Fermi" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ed153d681..195763949 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7157,6 +7157,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto true, //true = we are reconstructing the tx (it was first constructed by the tx proposer) ptx.tx_key, ptx.additional_tx_keys, + ptx.multisig_tx_key_entropy, ptx.tx ), error::wallet_internal_error, @@ -8063,8 +8064,13 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> has_rct = true; max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); } - const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets)); - if (has_rct_distribution) + + if (has_rct && rct_offsets.empty()) { + THROW_WALLET_EXCEPTION_IF(!get_rct_distribution(rct_start_height, rct_offsets), + error::get_output_distribution, "Could not obtain output distribution."); + } + + if (has_rct) { // check we're clear enough of rct start, to avoid corner cases below THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, @@ -8076,11 +8082,11 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // get histogram for the amounts we need cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t); - // request histogram for all outputs, except 0 if we have the rct distribution + // request histogram for all pre-rct outputs req_t.amounts.reserve(selected_transfers.size()); for(size_t idx: selected_transfers) - if (!m_transfers[idx].is_rct() || !has_rct_distribution) - req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); + if (!m_transfers[idx].is_rct()) + req_t.amounts.push_back(m_transfers[idx].amount()); if (!req_t.amounts.empty()) { std::sort(req_t.amounts.begin(), req_t.amounts.end()); @@ -8180,7 +8186,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp); std::unique_ptr<gamma_picker> gamma; - if (has_rct_distribution) + if (has_rct) gamma.reset(new gamma_picker(rct_offsets)); size_t num_selected_transfers = 0; @@ -8195,7 +8201,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); size_t start = req.outputs.size(); - bool use_histogram = amount != 0 || !has_rct_distribution; + bool use_histogram = amount != 0; const bool output_is_pre_fork = td.m_block_height < segregation_fork_height; uint64_t num_outs = 0, num_recent_outs = 0; @@ -8382,7 +8388,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t i; const char *type = ""; - if (amount == 0 && has_rct_distribution) + if (amount == 0) { THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker"); // gamma distribution @@ -8544,7 +8550,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> break; } } - bool use_histogram = amount != 0 || !has_rct_distribution; + bool use_histogram = amount != 0; if (!use_histogram) num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE]; @@ -9006,6 +9012,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; + crypto::secret_key multisig_tx_key_entropy; LOG_PRINT_L2("constructing tx"); auto sources_copy = sources; multisig::signing::tx_builder_ringct_t multisig_tx_builder; @@ -9029,6 +9036,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry false, tx_key, additional_tx_keys, + multisig_tx_key_entropy, tx ), error::wallet_internal_error, @@ -9155,6 +9163,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.additional_tx_keys = additional_tx_keys; ptx.dests = dsts; ptx.multisig_sigs = multisig_sigs; + ptx.multisig_tx_key_entropy = multisig_tx_key_entropy; ptx.construction_data.sources = sources_copy; ptx.construction_data.change_dts = change_dts; ptx.construction_data.splitted_dsts = splitted_dsts; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 836373939..16e898ad8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -494,6 +494,7 @@ private: struct confirmed_transfer_details { + cryptonote::transaction_prefix m_tx; uint64_t m_amount_in; uint64_t m_amount_out; uint64_t m_change; @@ -508,10 +509,12 @@ private: confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0), m_subaddr_account((uint32_t)-1) {} confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height): - m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices), m_rings(utd.m_rings) {} + m_tx(utd.m_tx), m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices), m_rings(utd.m_rings) {} BEGIN_SERIALIZE_OBJECT() - VERSION_FIELD(0) + VERSION_FIELD(1) + if (version >= 1) + FIELD(m_tx) VARINT_FIELD(m_amount_in) VARINT_FIELD(m_amount_out) VARINT_FIELD(m_change) @@ -632,10 +635,12 @@ private: std::vector<crypto::secret_key> additional_tx_keys; std::vector<cryptonote::tx_destination_entry> dests; std::vector<multisig_sig> multisig_sigs; + crypto::secret_key multisig_tx_key_entropy; tx_construction_data construction_data; BEGIN_SERIALIZE_OBJECT() + VERSION_FIELD(1) FIELD(tx) FIELD(dust) FIELD(fee) @@ -648,6 +653,12 @@ private: FIELD(dests) FIELD(construction_data) FIELD(multisig_sigs) + if (version < 1) + { + multisig_tx_key_entropy = crypto::null_skey; + return true; + } + FIELD(multisig_tx_key_entropy) END_SERIALIZE() }; |