diff options
73 files changed, 2949 insertions, 548 deletions
diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index 5573d591a..2d6e64e7a 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -47,7 +47,6 @@ static size_t query_page_size() MERROR("Failed to determine page size"); return 0; } - MINFO("Page size: " << ret); return ret; #else #warning Missing query_page_size implementation diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index be0ffeac3..682857157 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -197,6 +197,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti uint64_t BlockchainDB::add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -241,7 +242,7 @@ uint64_t BlockchainDB::add_block( const block& blk // call out to subclass implementation to add the block & metadata time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); + add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); TIME_MEASURE_FINISH(time1); time_add_block1 += time1; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 396ae7544..6cd4cf346 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -359,12 +359,14 @@ private: * * @param blk the block to be added * @param block_weight the weight of the block (transactions and all) + * @param long_term_block_weight the long term block weight of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param blk_hash the hash of the block */ virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -376,7 +378,7 @@ private: * * The subclass implementing this will remove the block data from the top * block in the chain. The data to be removed is that which was added in - * BlockchainDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) + * BlockchainDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) * * If any of this cannot be done, the subclass should throw the corresponding * subclass of DB_EXCEPTION @@ -790,6 +792,7 @@ public: * * @param blk the block to be added * @param block_weight the size of the block (transactions and all) + * @param long_term_block_weight the long term weight of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param txs the transactions in the block @@ -798,6 +801,7 @@ public: */ virtual uint64_t add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -986,6 +990,17 @@ public: virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0; /** + * @brief fetch a block's long term weight + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param height the height requested + * + * @return the long term weight + */ + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const = 0; + + /** * @brief fetch a block's hash * * The subclass should return hash of the block with the diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index bd91f308a..747d30cf8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -30,6 +30,7 @@ #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <boost/current_function.hpp> +#include <boost/circular_buffer.hpp> #include <memory> // std::unique_ptr #include <cstring> // memcpy #include <random> @@ -54,7 +55,7 @@ using epee::string_tools::pod_to_hex; using namespace crypto; // Increase when the DB structure changes -#define VERSION 3 +#define VERSION 4 namespace { @@ -251,7 +252,7 @@ inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi namespace cryptonote { -typedef struct mdb_block_info_old +typedef struct mdb_block_info_1 { uint64_t bi_height; uint64_t bi_timestamp; @@ -259,9 +260,9 @@ typedef struct mdb_block_info_old uint64_t bi_weight; // a size_t really but we need 32-bit compat difficulty_type bi_diff; crypto::hash bi_hash; -} mdb_block_info_old; +} mdb_block_info_1; -typedef struct mdb_block_info +typedef struct mdb_block_info_2 { uint64_t bi_height; uint64_t bi_timestamp; @@ -270,7 +271,21 @@ typedef struct mdb_block_info difficulty_type bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; -} mdb_block_info; +} mdb_block_info_2; + +typedef struct mdb_block_info_3 +{ + uint64_t bi_height; + uint64_t bi_timestamp; + uint64_t bi_coins; + uint64_t bi_weight; // a size_t really but we need 32-bit compat + difficulty_type bi_diff; + crypto::hash bi_hash; + uint64_t bi_cum_rct; + uint64_t bi_long_term_block_weight; +} mdb_block_info_3; + +typedef mdb_block_info_3 mdb_block_info; typedef struct blk_height { crypto::hash bh_hash; @@ -677,7 +692,7 @@ estim: return threshold_size; } -void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, const crypto::hash& blk_hash) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -736,6 +751,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const diff const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data; bi.bi_cum_rct += bi_prev->bi_cum_rct; } + bi.bi_long_term_block_weight = long_term_block_weight; MDB_val_set(val, bi); result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP); @@ -2105,6 +2121,29 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh return ret; } +uint64_t BlockchainLMDB::get_block_long_term_weight(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(block_info); + + MDB_val_set(result, height); + auto get_result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &result, MDB_GET_BOTH); + if (get_result == MDB_NOTFOUND) + { + throw0(BLOCK_DNE(std::string("Attempt to get block long term weight from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block info not in db").c_str())); + } + else if (get_result) + throw0(DB_ERROR("Error attempting to retrieve a long term block weight from the db")); + + mdb_block_info *bi = (mdb_block_info *)result.mv_data; + uint64_t ret = bi->bi_long_term_block_weight; + TXN_POSTFIX_RDONLY(); + return ret; +} + crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3098,7 +3137,7 @@ void BlockchainLMDB::block_txn_abort() } } -uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3117,7 +3156,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const try { - BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs); + BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs); } catch (const DB_ERROR_TXN_START& e) { @@ -3655,7 +3694,7 @@ void BlockchainLMDB::migrate_0_1() break; } MDB_dbi diffs, hashes, sizes, timestamps; - mdb_block_info_old bi; + mdb_block_info_1 bi; MDB_val_set(nv, bi); lmdb_db_open(txn, "block_diffs", 0, diffs, "Failed to open db handle for block_diffs"); @@ -4282,8 +4321,8 @@ void BlockchainLMDB::migrate_2_3() } else if (result) throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); - const mdb_block_info_old *bi_old = (const mdb_block_info_old*)v.mv_data; - mdb_block_info bi; + const mdb_block_info_1 *bi_old = (const mdb_block_info_1*)v.mv_data; + mdb_block_info_2 bi; bi.bi_height = bi_old->bi_height; bi.bi_timestamp = bi_old->bi_timestamp; bi.bi_coins = bi_old->bi_coins; @@ -4336,6 +4375,161 @@ void BlockchainLMDB::migrate_2_3() txn.commit(); } +void BlockchainLMDB::migrate_3_4() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + bool past_long_term_weight = false; + + MGINFO_YELLOW("Migrating blockchain from DB version 3 to 4 - this may take a while:"); + + do { + LOG_PRINT_L1("migrating block info:"); + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + + MDB_stat db_stats; + if ((result = mdb_stat(txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + const uint64_t blockchain_height = db_stats.ms_entries; + + boost::circular_buffer<uint64_t> long_term_block_weights(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE); + + /* the block_info table name is the same but the old version and new version + * have incompatible data. Create a new table. We want the name to be similar + * to the old name so that it will occupy the same location in the DB. + */ + MDB_dbi o_block_info = m_block_info; + lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + + MDB_cursor *c_blocks; + result = mdb_cursor_open(txn, m_blocks, &c_blocks); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str())); + + MDB_cursor *c_old, *c_cur; + i = 0; + while(1) { + if (!(i % 1000)) { + if (i) { + LOGIF(el::Level::Info) { + std::cout << i << " / " << blockchain_height << " \r" << std::flush; + } + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + } + result = mdb_cursor_open(txn, m_block_info, &c_cur); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str())); + result = mdb_cursor_open(txn, o_block_info, &c_old); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str())); + if (!i) { + MDB_stat db_stat; + result = mdb_stat(txn, m_block_info, &db_stats); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str())); + i = db_stats.ms_entries; + } + } + result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT); + if (result == MDB_NOTFOUND) { + txn.commit(); + break; + } + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); + const mdb_block_info_2 *bi_old = (const mdb_block_info_2*)v.mv_data; + mdb_block_info_3 bi; + bi.bi_height = bi_old->bi_height; + bi.bi_timestamp = bi_old->bi_timestamp; + bi.bi_coins = bi_old->bi_coins; + bi.bi_weight = bi_old->bi_weight; + bi.bi_diff = bi_old->bi_diff; + bi.bi_hash = bi_old->bi_hash; + bi.bi_cum_rct = bi_old->bi_cum_rct; + + // get block major version to determine which rule is in place + if (!past_long_term_weight) + { + MDB_val kb, vb; + result = mdb_cursor_get(c_blocks, &k, &v, MDB_NEXT); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + if (vb.mv_size == 0) + throw0(DB_ERROR("Invalid data from m_blocks")); + const uint8_t block_major_version = *((const uint8_t*)vb.mv_data); + if (block_major_version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + past_long_term_weight = true; + } + + uint64_t long_term_block_weight; + if (past_long_term_weight) + { + std::vector<uint64_t> weights(long_term_block_weights.begin(), long_term_block_weights.end()); + uint64_t long_term_effective_block_median_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, epee::misc_utils::median(weights)); + long_term_block_weight = std::min<uint64_t>(bi.bi_weight, long_term_effective_block_median_weight + long_term_effective_block_median_weight * 2 / 5); + } + else + { + long_term_block_weight = bi.bi_weight; + } + long_term_block_weights.push_back(long_term_block_weight); + bi.bi_long_term_block_weight = long_term_block_weight; + + MDB_val_set(nv, bi); + result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str())); + /* we delete the old records immediately, so the overall DB and mapsize should not grow. + * This is a little slower than just letting mdb_drop() delete it all at the end, but + * it saves a significant amount of disk space. + */ + result = mdb_cursor_del(c_old, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str())); + i++; + } + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + /* Delete the old table */ + result = mdb_drop(txn, o_block_info, 1); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); + + RENAME_DB("block_infn"); + + lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + txn.commit(); + } while(0); + + uint32_t version = 4; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_copy<const char *> vk("version"); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { switch(oldversion) { @@ -4345,6 +4539,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion) migrate_1_2(); /* FALLTHRU */ case 2: migrate_2_3(); /* FALLTHRU */ + case 3: + migrate_3_4(); /* FALLTHRU */ default: ; } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index e1f748ed8..923507d06 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -213,6 +213,8 @@ public: virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const; + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const; + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const; virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const; @@ -274,6 +276,7 @@ public: virtual uint64_t add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -318,6 +321,7 @@ private: virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -396,6 +400,9 @@ private: // migrate from DB version 2 to 3 void migrate_2_3(); + // migrate from DB version 3 to 4 + void migrate_3_4(); + void cleanup_batch(); private: diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h new file mode 100644 index 000000000..0476e5e1b --- /dev/null +++ b/src/blockchain_db/testdb.h @@ -0,0 +1,152 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include <string> +#include <vector> +#include <map> + +#include "blockchain_db.h" + +namespace cryptonote +{ + +class BaseTestDB: public cryptonote::BlockchainDB { +public: + BaseTestDB() {} + virtual void open(const std::string& filename, const int db_flags = 0) { } + virtual void close() {} + virtual void sync() {} + virtual void safesyncmode(const bool onoff) {} + virtual void reset() {} + virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); } + virtual bool remove_data_file(const std::string& folder) const { return true; } + virtual std::string get_db_name() const { return std::string(); } + virtual bool lock() { return true; } + virtual void unlock() { } + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } + virtual void batch_stop() {} + virtual void set_batch_transactions(bool) {} + virtual void block_txn_start(bool readonly=false) {} + virtual void block_txn_stop() {} + virtual void block_txn_abort() {} + virtual void drop_hard_fork_info() {} + virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); } + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } + virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } + virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); } + virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } + virtual uint64_t get_top_block_timestamp() const { return 0; } + virtual size_t get_block_weight(const uint64_t& height) const { return 128; } + virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } + virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const { return 128; } + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } + virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); } + virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } + virtual crypto::hash top_block_hash() const { return crypto::hash(); } + virtual cryptonote::block get_top_block() const { return cryptonote::block(); } + virtual uint64_t height() const { return 1; } + virtual bool tx_exists(const crypto::hash& h) const { return false; } + virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } + virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } + virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); } + virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; } + virtual uint64_t get_tx_count() const { return 0; } + virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const { return std::vector<cryptonote::transaction>(); } + virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } + virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } + virtual uint64_t get_indexing_base() const { return 0; } + virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return cryptonote::output_data_t(); } + virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {} + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) {} + virtual bool can_thread_bulk_indices() const { return false; } + virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); } + virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector<uint64_t>(); } + virtual bool has_key_image(const crypto::key_image& img) const { return false; } + virtual void remove_block() { } + virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} + virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {} + virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} + virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {} + virtual void add_spent_key(const crypto::key_image& k_image) {} + virtual void remove_spent_key(const crypto::key_image& k_image) {} + + virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const { return true; } + virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const { return true; } + virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const { return true; } + virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const { return true; } + virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const { return true; } + virtual bool is_read_only() const { return false; } + virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); } + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const { return false; } + + virtual void add_txpool_tx(const cryptonote::transaction &tx, const cryptonote::txpool_tx_meta_t& details) {} + virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {} + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } + virtual void remove_txpool_tx(const crypto::hash& txid) {} + virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } + virtual uint64_t get_database_size() const { return 0; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) { } + virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) {} + virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + virtual void check_hard_fork_info() {} + + virtual uint32_t get_blockchain_pruning_seed() const { return 0; } + virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } + virtual bool update_pruning() { return true; } + virtual bool check_pruning() { return true; } + virtual void prune_outputs(uint64_t amount) {} +}; + +} diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 9ec768d26..b78b8b5c9 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -484,7 +484,8 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path try { - core.get_blockchain_storage().get_db().add_block(b, block_weight, cumulative_difficulty, coins_generated, txs); + uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight); + core.get_blockchain_storage().get_db().add_block(b, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs); } catch (const std::exception& e) { diff --git a/src/common/notify.cpp b/src/common/notify.cpp index cadc68ea7..b2316a3b1 100644 --- a/src/common/notify.cpp +++ b/src/common/notify.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <boost/algorithm/string.hpp> +#include <stdarg.h> #include "misc_log_ex.h" #include "file_io_utils.h" #include "spawn.h" @@ -44,17 +45,32 @@ Notify::Notify(const char *spec) { CHECK_AND_ASSERT_THROW_MES(spec, "Null spec"); - boost::split(args, spec, boost::is_any_of(" ")); + boost::split(args, spec, boost::is_any_of(" \t"), boost::token_compress_on); CHECK_AND_ASSERT_THROW_MES(args.size() > 0, "Failed to parse spec"); filename = args[0]; CHECK_AND_ASSERT_THROW_MES(epee::file_io_utils::is_file_exist(filename), "File not found: " << filename); } -int Notify::notify(const char *parameter) +static void replace(std::vector<std::string> &v, const char *tag, const char *s) +{ + for (std::string &str: v) + boost::replace_all(str, tag, s); +} + +int Notify::notify(const char *tag, const char *s, ...) { std::vector<std::string> margs = args; - for (std::string &s: margs) - boost::replace_all(s, "%s", parameter); + + replace(margs, tag, s); + + va_list ap; + va_start(ap, s); + while ((tag = va_arg(ap, const char*))) + { + s = va_arg(ap, const char*); + replace(margs, tag, s); + } + va_end(ap); return tools::spawn(filename.c_str(), margs, false); } diff --git a/src/common/notify.h b/src/common/notify.h index 81aacebb0..f813e8def 100644 --- a/src/common/notify.h +++ b/src/common/notify.h @@ -39,7 +39,7 @@ class Notify public: Notify(const char *spec); - int notify(const char *parameter); + int notify(const char *tag, const char *s, ...); private: std::string filename; diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp index 0a2ce8387..b2d03f62f 100644 --- a/src/common/spawn.cpp +++ b/src/common/spawn.cpp @@ -35,6 +35,7 @@ #include <windows.h> #else #include <sys/wait.h> +#include <signal.h> #endif #include "misc_log_ex.h" @@ -114,7 +115,10 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait) if (pid > 0) { if (!wait) + { + signal(SIGCHLD, SIG_IGN); return 0; + } while (1) { diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 6e85ad0e9..0610f7051 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -73,18 +73,18 @@ namespace crypto { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d77d55cf3..891896120 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -79,7 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed); +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 995e2294e..165fe6bb0 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -71,12 +71,12 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/, height); } - inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/); + inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/, height); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 40cfb0461..164cff3d4 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -39,6 +39,7 @@ #include "hash-ops.h" #include "oaes_lib.h" #include "variant2_int_sqrt.h" +#include "variant4_random_math.h" #define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) @@ -47,8 +48,8 @@ #define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +extern void aesb_single_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); #define VARIANT1_1(p) \ do if (variant == 1) \ @@ -109,69 +110,96 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \ xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \ xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \ - division_result = state.hs.w[12]; \ - sqrt_result = state.hs.w[13]; \ + division_result = SWAP64LE(state.hs.w[12]); \ + sqrt_result = SWAP64LE(state.hs.w[13]); \ } while (0) #define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \ do if (variant >= 2) \ { \ - const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ + __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \ const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \ + if (variant >= 4) \ + { \ + chunk1 = _mm_xor_si128(chunk1, chunk2); \ + _c = _mm_xor_si128(_c, chunk3); \ + _c = _mm_xor_si128(_c, chunk1); \ + } \ } while (0) #define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset) \ do if (variant >= 2) \ { \ - const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \ + uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \ const uint64x2_t chunk2 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x20))); \ const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x30))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \ + if (variant >= 4) \ + { \ + chunk1 = veorq_u64(chunk1, chunk2); \ + _c = vreinterpretq_u8_u64(veorq_u64(vreinterpretq_u64_u8(_c), chunk3)); \ + _c = vreinterpretq_u8_u64(veorq_u64(vreinterpretq_u64_u8(_c), chunk1)); \ + } \ } while (0) -#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset) \ +#define VARIANT2_PORTABLE_SHUFFLE_ADD(out, a_, base_ptr, offset) \ do if (variant >= 2) \ { \ uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \ uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \ uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \ \ - const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \ + uint64_t chunk1_old[2] = { SWAP64LE(chunk1[0]), SWAP64LE(chunk1[1]) }; \ + const uint64_t chunk2_old[2] = { SWAP64LE(chunk2[0]), SWAP64LE(chunk2[1]) }; \ + const uint64_t chunk3_old[2] = { SWAP64LE(chunk3[0]), SWAP64LE(chunk3[1]) }; \ \ uint64_t b1[2]; \ - memcpy(b1, b + 16, 16); \ - chunk1[0] = chunk3[0] + b1[0]; \ - chunk1[1] = chunk3[1] + b1[1]; \ + memcpy_swap64le(b1, b + 16, 2); \ + chunk1[0] = SWAP64LE(chunk3_old[0] + b1[0]); \ + chunk1[1] = SWAP64LE(chunk3_old[1] + b1[1]); \ \ uint64_t a0[2]; \ - memcpy(a0, a, 16); \ - chunk3[0] = chunk2[0] + a0[0]; \ - chunk3[1] = chunk2[1] + a0[1]; \ + memcpy_swap64le(a0, a_, 2); \ + chunk3[0] = SWAP64LE(chunk2_old[0] + a0[0]); \ + chunk3[1] = SWAP64LE(chunk2_old[1] + a0[1]); \ \ uint64_t b0[2]; \ - memcpy(b0, b, 16); \ - chunk2[0] = chunk1_old[0] + b0[0]; \ - chunk2[1] = chunk1_old[1] + b0[1]; \ + memcpy_swap64le(b0, b, 2); \ + chunk2[0] = SWAP64LE(chunk1_old[0] + b0[0]); \ + chunk2[1] = SWAP64LE(SWAP64LE(chunk1_old[1]) + b0[1]); \ + if (variant >= 4) \ + { \ + uint64_t out_copy[2]; \ + memcpy_swap64le(out_copy, out, 2); \ + chunk1_old[0] ^= chunk2_old[0]; \ + chunk1_old[1] ^= chunk2_old[1]; \ + out_copy[0] ^= chunk3_old[0]; \ + out_copy[1] ^= chunk3_old[1]; \ + out_copy[0] ^= chunk1_old[0]; \ + out_copy[1] ^= chunk1_old[1]; \ + memcpy_swap64le(out, out_copy, 2); \ + } \ } while (0) #define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ - ((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \ + uint64_t tmpx = division_result ^ (sqrt_result << 32); \ + ((uint64_t*)(b))[0] ^= SWAP64LE(tmpx); \ { \ - const uint64_t dividend = ((uint64_t*)(ptr))[1]; \ - const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ + const uint64_t dividend = SWAP64LE(((uint64_t*)(ptr))[1]); \ + const uint32_t divisor = (SWAP64LE(((uint64_t*)(ptr))[0]) + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ division_result = ((uint32_t)(dividend / divisor)) + \ (((uint64_t)(dividend % divisor)) << 32); \ } \ - const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result + const uint64_t sqrt_input = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result #define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); \ @@ -181,7 +209,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50) // double precision floating point type has enough bits of precision on current platform #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); \ @@ -191,7 +219,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp // double precision floating point type is not good enough on current platform // fall back to the reference code (integer only) #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \ @@ -199,18 +227,70 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #endif #define VARIANT2_2_PORTABLE() \ - if (variant >= 2) { \ + if (variant == 2 || variant == 3) { \ xor_blocks(long_state + (j ^ 0x10), d); \ xor_blocks(d, long_state + (j ^ 0x20)); \ } #define VARIANT2_2() \ - do if (variant >= 2) \ + do if (variant == 2 || variant == 3) \ { \ - *U64(hp_state + (j ^ 0x10)) ^= hi; \ - *(U64(hp_state + (j ^ 0x10)) + 1) ^= lo; \ - hi ^= *U64(hp_state + (j ^ 0x20)); \ - lo ^= *(U64(hp_state + (j ^ 0x20)) + 1); \ + *U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ + *(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ + hi ^= SWAP64LE(*U64(hp_state + (j ^ 0x20))); \ + lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ + } while (0) + +#define V4_REG_LOAD(dst, src) \ + do { \ + memcpy((dst), (src), sizeof(v4_reg)); \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + *(dst) = SWAP32LE(*(dst)); \ + else \ + *(dst) = SWAP64LE(*(dst)); \ + } while (0) + +#define VARIANT4_RANDOM_MATH_INIT() \ + v4_reg r[9]; \ + struct V4_Instruction code[NUM_INSTRUCTIONS_MAX + 1]; \ + do if (variant >= 4) \ + { \ + for (int i = 0; i < 4; ++i) \ + V4_REG_LOAD(r + i, (uint8_t*)(state.hs.w + 12) + sizeof(v4_reg) * i); \ + v4_random_math_init(code, height); \ + } while (0) + +#define VARIANT4_RANDOM_MATH(a, b, r, _b, _b1) \ + do if (variant >= 4) \ + { \ + uint64_t t[2]; \ + memcpy(t, b, sizeof(uint64_t)); \ + \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + t[0] ^= SWAP64LE((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \ + else \ + t[0] ^= SWAP64LE((r[0] + r[1]) ^ (r[2] + r[3])); \ + \ + memcpy(b, t, sizeof(uint64_t)); \ + \ + V4_REG_LOAD(r + 4, a); \ + V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \ + V4_REG_LOAD(r + 6, _b); \ + V4_REG_LOAD(r + 7, _b1); \ + V4_REG_LOAD(r + 8, (uint64_t*)(_b1) + 1); \ + \ + v4_random_math(code, r); \ + \ + memcpy(t, a, sizeof(uint64_t) * 2); \ + \ + if (sizeof(v4_reg) == sizeof(uint32_t)) { \ + t[0] ^= SWAP64LE(r[2] | ((uint64_t)(r[3]) << 32)); \ + t[1] ^= SWAP64LE(r[0] | ((uint64_t)(r[1]) << 32)); \ + } else { \ + t[0] ^= SWAP64LE(r[2] ^ r[3]); \ + t[1] ^= SWAP64LE(r[0] ^ r[1]); \ + } \ + memcpy(a, t, sizeof(uint64_t) * 2); \ } while (0) @@ -297,6 +377,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_INTEGER_MATH_SSE2(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ @@ -693,7 +774,7 @@ void slow_hash_free_state(void) * @param length the length in bytes of the data * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -729,6 +810,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -900,6 +982,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ @@ -1062,7 +1145,7 @@ STATIC INLINE void aligned_free(void *ptr) } #endif /* FORCE_USE_HEAP */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; @@ -1099,6 +1182,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -1277,10 +1361,11 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; + uint8_t a1[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE * 2]; uint8_t c[AES_BLOCK_SIZE]; uint8_t c1[AES_BLOCK_SIZE]; @@ -1316,6 +1401,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); // use aligned data memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); @@ -1339,10 +1425,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int // Iteration 1 j = state_index(a); p = &long_state[j]; - aesb_single_round(p, p, a); - copy_block(c1, p); + aesb_single_round(p, c1, a); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + copy_block(p, c1); xor_blocks(p, b); VARIANT1_1(p); @@ -1351,13 +1437,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int p = &long_state[j]; copy_block(c, p); + copy_block(a1, a); VARIANT2_PORTABLE_INTEGER_MATH(c, c1); + VARIANT4_RANDOM_MATH(a1, c, r, b, b + AES_BLOCK_SIZE); mul(c1, c, d); VARIANT2_2_PORTABLE(); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); - sum_half_blocks(a, d); - swap_blocks(a, c); - xor_blocks(a, c); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + sum_half_blocks(a1, d); + swap_blocks(a1, c); + xor_blocks(a1, c); VARIANT1_2(U64(c) + 1); copy_block(p, c); @@ -1365,6 +1453,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int copy_block(b + AES_BLOCK_SIZE, b); } copy_block(b, c1); + copy_block(a, a1); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1408,10 +1497,7 @@ static void (*const extra_hashes[4])(const void *, size_t, char *) = { hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); - -static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } +static size_t e2i(const uint8_t* a, size_t count) { return (SWAP64LE(*((uint64_t*)a)) / AES_BLOCK_SIZE) & (count - 1); } static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { uint64_t a0, b0; @@ -1478,7 +1564,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else @@ -1488,6 +1574,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; + uint8_t a1[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE * 2]; uint8_t c1[AES_BLOCK_SIZE]; uint8_t c2[AES_BLOCK_SIZE]; @@ -1507,6 +1594,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_PORTABLE_INIT(); VARIANT2_PORTABLE_INIT(); + VARIANT4_RANDOM_MATH_INIT(); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { @@ -1530,7 +1618,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; copy_block(c1, &long_state[j]); aesb_single_round(c1, c1, a); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); copy_block(&long_state[j], c1); xor_blocks(&long_state[j], b); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); @@ -1538,22 +1626,22 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int /* Iteration 2 */ j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; copy_block(c2, &long_state[j]); + copy_block(a1, a); VARIANT2_PORTABLE_INTEGER_MATH(c2, c1); + VARIANT4_RANDOM_MATH(a1, c2, r, b, b + AES_BLOCK_SIZE); mul(c1, c2, d); VARIANT2_2_PORTABLE(); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); - swap_blocks(a, c1); - sum_half_blocks(c1, d); - swap_blocks(c1, c2); - xor_blocks(c1, c2); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + sum_half_blocks(a1, d); + swap_blocks(a1, c2); + xor_blocks(a1, c2); VARIANT1_2(c2 + 8); copy_block(&long_state[j], c2); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); if (variant >= 2) { copy_block(b + AES_BLOCK_SIZE, b); } - copy_block(b, a); - copy_block(a, c1); + copy_block(b, c1); + copy_block(a, a1); } memcpy(text, state.init, INIT_SIZE_BYTE); diff --git a/src/crypto/variant4_random_math.h b/src/crypto/variant4_random_math.h new file mode 100644 index 000000000..be5447395 --- /dev/null +++ b/src/crypto/variant4_random_math.h @@ -0,0 +1,441 @@ +#ifndef VARIANT4_RANDOM_MATH_H +#define VARIANT4_RANDOM_MATH_H + +// Register size can be configured to either 32 bit (uint32_t) or 64 bit (uint64_t) +typedef uint32_t v4_reg; + +enum V4_Settings +{ + // Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications + TOTAL_LATENCY = 15 * 3, + + // Always generate at least 60 instructions + NUM_INSTRUCTIONS_MIN = 60, + + // Never generate more than 70 instructions (final RET instruction doesn't count here) + NUM_INSTRUCTIONS_MAX = 70, + + // Available ALUs for MUL + // Modern CPUs typically have only 1 ALU which can do multiplications + ALU_COUNT_MUL = 1, + + // Total available ALUs + // Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code + ALU_COUNT = 3, +}; + +enum V4_InstructionList +{ + MUL, // a*b + ADD, // a+b + C, C is an unsigned 32-bit constant + SUB, // a-b + ROR, // rotate right "a" by "b & 31" bits + ROL, // rotate left "a" by "b & 31" bits + XOR, // a^b + RET, // finish execution + V4_INSTRUCTION_COUNT = RET, +}; + +// V4_InstructionDefinition is used to generate code from random data +// Every random sequence of bytes is a valid code +// +// There are 9 registers in total: +// - 4 variable registers +// - 5 constant registers initialized from loop variables +// This is why dst_index is 2 bits +enum V4_InstructionDefinition +{ + V4_OPCODE_BITS = 3, + V4_DST_INDEX_BITS = 2, + V4_SRC_INDEX_BITS = 3, +}; + +struct V4_Instruction +{ + uint8_t opcode; + uint8_t dst_index; + uint8_t src_index; + uint32_t C; +}; + +#ifndef FORCEINLINE +#if defined(__GNUC__) +#define FORCEINLINE __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE inline +#endif +#endif + +#ifndef UNREACHABLE_CODE +#if defined(__GNUC__) +#define UNREACHABLE_CODE __builtin_unreachable() +#elif defined(_MSC_VER) +#define UNREACHABLE_CODE __assume(false) +#else +#define UNREACHABLE_CODE +#endif +#endif + +// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU: +// every switch-case will point to the same destination on every iteration of Cryptonight main loop +// +// This is about as fast as it can get without using low-level machine code generation +static FORCEINLINE void v4_random_math(const struct V4_Instruction* code, v4_reg* r) +{ + enum + { + REG_BITS = sizeof(v4_reg) * 8, + }; + +#define V4_EXEC(i) \ + { \ + const struct V4_Instruction* op = code + i; \ + const v4_reg src = r[op->src_index]; \ + v4_reg* dst = r + op->dst_index; \ + switch (op->opcode) \ + { \ + case MUL: \ + *dst *= src; \ + break; \ + case ADD: \ + *dst += src + op->C; \ + break; \ + case SUB: \ + *dst -= src; \ + break; \ + case ROR: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case ROL: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case XOR: \ + *dst ^= src; \ + break; \ + case RET: \ + return; \ + default: \ + UNREACHABLE_CODE; \ + break; \ + } \ + } + +#define V4_EXEC_10(j) \ + V4_EXEC(j + 0) \ + V4_EXEC(j + 1) \ + V4_EXEC(j + 2) \ + V4_EXEC(j + 3) \ + V4_EXEC(j + 4) \ + V4_EXEC(j + 5) \ + V4_EXEC(j + 6) \ + V4_EXEC(j + 7) \ + V4_EXEC(j + 8) \ + V4_EXEC(j + 9) + + // Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency + // I've checked all block heights < 10,000,000 and here is the distribution of program sizes: + // + // 60 27960 + // 61 105054 + // 62 2452759 + // 63 5115997 + // 64 1022269 + // 65 1109635 + // 66 153145 + // 67 8550 + // 68 4529 + // 69 102 + + // Unroll 70 instructions here + V4_EXEC_10(0); // instructions 0-9 + V4_EXEC_10(10); // instructions 10-19 + V4_EXEC_10(20); // instructions 20-29 + V4_EXEC_10(30); // instructions 30-39 + V4_EXEC_10(40); // instructions 40-49 + V4_EXEC_10(50); // instructions 50-59 + V4_EXEC_10(60); // instructions 60-69 + +#undef V4_EXEC_10 +#undef V4_EXEC +} + +// If we don't have enough data available, generate more +static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size) +{ + if (*data_index + bytes_needed > data_size) + { + hash_extra_blake(data, data_size, (char*) data); + *data_index = 0; + } +} + +// Generates as many random math operations as possible with given latency and ALU restrictions +// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions +static inline int v4_random_math_init(struct V4_Instruction* code, const uint64_t height) +{ + // MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle + // These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake + // + // AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors + // Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors + // AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same + // Source: https://www.agner.org/optimize/instruction_tables.pdf + const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 }; + + // Instruction latencies for theoretical ASIC implementation + const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 }; + + // Available ALUs for each instruction + const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT }; + + int8_t data[32]; + memset(data, 0, sizeof(data)); + uint64_t tmp = SWAP64LE(height); + memcpy(data, &tmp, sizeof(uint64_t)); + data[20] = 0xda; // change seed + + // Set data_index past the last byte in data + // to trigger full data update with blake hash + // before we start using it + size_t data_index = sizeof(data); + + int code_size; + + // There is a small chance (1.8%) that register R8 won't be used in the generated program + // So we keep track of it and try again if it's not used + bool r8_used; + do { + int latency[9]; + int asic_latency[9]; + + // Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution + // byte 0: current value of the destination register + // byte 1: instruction opcode + // byte 2: current value of the source register + // + // Registers R4-R8 are constant and are treated as having the same value because when we do + // the same operation twice with two constant source registers, it can be optimized into a single operation + uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }; + + bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT]; + bool is_rotation[V4_INSTRUCTION_COUNT]; + bool rotated[4]; + int rotate_count = 0; + + memset(latency, 0, sizeof(latency)); + memset(asic_latency, 0, sizeof(asic_latency)); + memset(alu_busy, 0, sizeof(alu_busy)); + memset(is_rotation, 0, sizeof(is_rotation)); + memset(rotated, 0, sizeof(rotated)); + is_rotation[ROR] = true; + is_rotation[ROL] = true; + + int num_retries = 0; + code_size = 0; + + int total_iterations = 0; + r8_used = false; + + // Generate random code to achieve minimal required latency for our abstract CPU + // Try to get this latency for all 4 registers + while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64)) + { + // Fail-safe to guarantee loop termination + ++total_iterations; + if (total_iterations > 256) + break; + + check_data(&data_index, 1, data, sizeof(data)); + + const uint8_t c = ((uint8_t*)data)[data_index++]; + + // MUL = opcodes 0-2 + // ADD = opcode 3 + // SUB = opcode 4 + // ROR/ROL = opcode 5, shift direction is selected randomly + // XOR = opcodes 6-7 + uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1); + if (opcode == 5) + { + check_data(&data_index, 1, data, sizeof(data)); + opcode = (data[data_index++] >= 0) ? ROR : ROL; + } + else if (opcode >= 6) + { + opcode = XOR; + } + else + { + opcode = (opcode <= 2) ? MUL : (opcode - 2); + } + + uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1); + uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1); + + const int a = dst_index; + int b = src_index; + + // Don't do ADD/SUB/XOR with the same register + if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b)) + { + // Use register R8 as source instead + b = 8; + src_index = 8; + } + + // Don't do rotation with the same destination twice because it's equal to a single rotation + if (is_rotation[opcode] && rotated[a]) + { + continue; + } + + // Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized: + // 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations + // 2xXOR(a, b) = NOP + if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16))) + { + continue; + } + + // Find which ALU is available (and when) for this instruction + int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b]; + int alu_index = -1; + while (next_latency < TOTAL_LATENCY) + { + for (int i = op_ALUs[opcode] - 1; i >= 0; --i) + { + if (!alu_busy[next_latency][i]) + { + // ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check + if ((opcode == ADD) && alu_busy[next_latency + 1][i]) + { + continue; + } + + // Rotation can only start when previous rotation is finished, so do an additional availability check + if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode])) + { + continue; + } + + alu_index = i; + break; + } + } + if (alu_index >= 0) + { + break; + } + ++next_latency; + } + + // Don't generate instructions that leave some register unchanged for more than 7 cycles + if (next_latency > latency[a] + 7) + { + continue; + } + + next_latency += op_latency[opcode]; + + if (next_latency <= TOTAL_LATENCY) + { + if (is_rotation[opcode]) + { + ++rotate_count; + } + + // Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined + alu_busy[next_latency - op_latency[opcode]][alu_index] = true; + latency[a] = next_latency; + + // ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple + asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode]; + + rotated[a] = is_rotation[opcode]; + + inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16); + + code[code_size].opcode = opcode; + code[code_size].dst_index = dst_index; + code[code_size].src_index = src_index; + code[code_size].C = 0; + + if (src_index == 8) + { + r8_used = true; + } + + if (opcode == ADD) + { + // ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too + alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true; + + // ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C" + check_data(&data_index, sizeof(uint32_t), data, sizeof(data)); + uint32_t t; + memcpy(&t, data + data_index, sizeof(uint32_t)); + code[code_size].C = SWAP32LE(t); + data_index += sizeof(uint32_t); + } + + ++code_size; + if (code_size >= NUM_INSTRUCTIONS_MIN) + { + break; + } + } + else + { + ++num_retries; + } + } + + // ASIC has more execution resources and can extract as much parallelism from the code as possible + // We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC + // Get this latency for at least 1 of the 4 registers + const int prev_code_size = code_size; + while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY)) + { + int min_idx = 0; + int max_idx = 0; + for (int i = 1; i < 4; ++i) + { + if (asic_latency[i] < asic_latency[min_idx]) min_idx = i; + if (asic_latency[i] > asic_latency[max_idx]) max_idx = i; + } + + const uint8_t pattern[3] = { ROR, MUL, MUL }; + const uint8_t opcode = pattern[(code_size - prev_code_size) % 3]; + latency[min_idx] = latency[max_idx] + op_latency[opcode]; + asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode]; + + code[code_size].opcode = opcode; + code[code_size].dst_index = min_idx; + code[code_size].src_index = max_idx; + code[code_size].C = 0; + ++code_size; + } + + // There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time + // It never does more than 4 iterations for all block heights < 10,000,000 + } while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX)); + + // It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here + // Add final instruction to stop the interpreter + code[code_size].opcode = RET; + code[code_size].dst_index = 0; + code[code_size].src_index = 0; + code[code_size].C = 0; + + return code_size; +} + +#endif diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 0725a2bb8..d1e321994 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -249,7 +249,6 @@ namespace boost { a & x.mask; a & x.amount; - // a & x.senderPk; // not serialized, as we do not use it in monero currently } template <class Archive> @@ -295,7 +294,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -323,7 +322,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -337,7 +336,7 @@ namespace boost if (x.p.rangeSigs.empty()) a & x.p.bulletproofs; a & x.p.MGs; - if (x.type == rct::RCTTypeBulletproof) + if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2) a & x.p.pseudoOuts; } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 5fcfa33f6..a82280c4d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1054,7 +1054,7 @@ namespace cryptonote } blobdata bd = get_block_hashing_blob(b); const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; - crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant); + crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, height); return true; } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index f05b25901..51efe9903 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -305,6 +305,29 @@ bool HardFork::rescan_from_chain_height(uint64_t height) return rescan_from_block_height(height - 1); } +void HardFork::on_block_popped(uint64_t nblocks) +{ + CHECK_AND_ASSERT_THROW_MES(nblocks > 0, "nblocks must be greater than 0"); + + CRITICAL_REGION_LOCAL(lock); + + const uint64_t new_chain_height = db.height(); + const uint64_t old_chain_height = new_chain_height + nblocks; + uint8_t version; + uint64_t height; + for (height = old_chain_height - 1; height >= new_chain_height; --height) + { + versions.pop_back(); + version = db.get_hard_fork_version(height); + versions.push_front(version); + } + + // does not take voting into account + for (current_fork_index = heights.size() - 1; current_fork_index > 0; --current_fork_index) + if (height >= heights[current_fork_index].height) + break; +} + int HardFork::get_voted_fork_index(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index a63a66976..a3fc25dfa 100644 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -150,6 +150,16 @@ namespace cryptonote bool reorganize_from_chain_height(uint64_t height); /** + * @brief called when one or more blocks are popped from the blockchain + * + * The current fork will be updated by looking up the db, + * which is much cheaper than recomputing everything + * + * @param new_chain_height the height of the chain after popping + */ + void on_block_popped(uint64_t new_chain_height); + + /** * @brief returns current state at the given time * * Based on the approximate time of the last known hard fork, diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a6858ce7c..147e1e7ce 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -58,6 +58,8 @@ #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 +#define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE 100000 // size in blocks of the long term block weight median window +#define CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR 50 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin @@ -138,6 +140,8 @@ #define HF_VERSION_MIN_MIXIN_10 8 #define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_PER_BYTE_FEE 8 +#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10 +#define HF_VERSION_SMALLER_BP 10 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fa23b6bd2..9f3ebf5aa 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -116,6 +116,9 @@ static const struct { // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. { 9, 1686275, 0, 1535889548 }, + + // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10. + { 10, 1788000, 0, 1549792439 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; @@ -140,6 +143,7 @@ static const struct { { 7, 1057027, 0, 1512211236 }, { 8, 1057058, 0, 1533211200 }, { 9, 1057778, 0, 1533297600 }, + { 10, 1154318, 0, 1550153694 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -161,12 +165,15 @@ static const struct { { 7, 37000, 0, 1521600000 }, { 8, 176456, 0, 1537821770 }, { 9, 177176, 0, 1537821771 }, + { 10, 269000, 0, 1550153694 }, }; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), + m_long_term_block_weights(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), + m_long_term_block_weights_height(0), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), m_btc_valid(false) @@ -492,6 +499,17 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); } + if (test_options && test_options->long_term_block_weight_window) + m_long_term_block_weights.set_capacity(test_options->long_term_block_weight_window); + + // fill up the long term block weights to reach the required amount + const uint64_t db_height = m_db->height(); + const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), db_height); + while (m_long_term_block_weights.size() < nblocks) + { + m_long_term_block_weights.push_front(db_height - 1 - m_long_term_block_weights.size()); + } + update_next_cumulative_weight_limit(); return true; } @@ -606,6 +624,11 @@ block Blockchain::pop_block_from_blockchain() throw; } + // make sure the hard fork object updates its current version + m_hardfork->on_block_popped(1); + + pop_from_long_term_block_weights(); + // return transactions from popped block to the tx_pool for (transaction& tx : popped_txs) { @@ -616,12 +639,7 @@ block Blockchain::pop_block_from_blockchain() // FIXME: HardFork // Besides the below, popping a block should also remove the last entry // in hf_versions. - // - // FIXME: HardFork - // This is not quite correct, as we really want to add the txes - // to the pool based on the version determined after all blocks - // are popped. - uint8_t version = get_current_hard_fork_version(); + uint8_t version = get_ideal_hard_fork_version(m_db->height()); // We assume that if they were in a block, the transactions are already // known to the network as a whole. However, if we had mined that block, @@ -655,6 +673,8 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) m_timestamps_and_difficulties_height = 0; m_alternative_chains.clear(); invalidate_block_template_cache(); + m_long_term_block_weights.clear(); + m_long_term_block_weights_height = 0; m_db->reset(); m_hardfork->init(); @@ -883,6 +903,17 @@ difficulty_type Blockchain::get_difficulty_for_next_block() return diff; } //------------------------------------------------------------------ +std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const +{ + uint64_t height = m_db->height(); + if (blocks > height) + blocks = height; + std::vector<time_t> timestamps(blocks); + while (blocks--) + timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1); + return timestamps; +} +//------------------------------------------------------------------ // This function removes blocks from the blockchain until it gets to the // position where the blockchain switch started and then re-adds the blocks // that had been removed. @@ -993,6 +1024,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: } // if we're to keep the disconnected blocks, add them as alternates + const size_t discarded_blocks = disconnected_chain.size(); if(!discard_disconnected_chain) { //pushing old chain as alternative chain @@ -1017,6 +1049,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: m_hardfork->reorganize_from_chain_height(split_height); + std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify; + if (reorg_notify) + reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(), + "%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL); + MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height()); return true; } @@ -1141,7 +1178,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - std::vector<size_t> last_blocks_weights; + std::vector<uint64_t> last_blocks_weights; get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version)) { @@ -1176,7 +1213,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } //------------------------------------------------------------------ // get the block weights of the last <count> blocks, and return by reference <sz>. -void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const +void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2384,6 +2421,18 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } + // from v10, allow bulletproofs v2 + if (hf_version < HF_VERSION_SMALLER_BP) { + if (tx.version >= 2) { + if (tx.rct_signatures.type == rct::RCTTypeBulletproof2) + { + MERROR_VER("Bulletproofs v2 are not allowed before v" << HF_VERSION_SMALLER_BP); + tvc.m_invalid_output = true; + return false; + } + } + } + return true; } //------------------------------------------------------------------ @@ -2424,7 +2473,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -2450,7 +2499,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -2521,7 +2570,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (hf_version >= HF_VERSION_MIN_MIXIN_10 && mixin != 10) + if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && mixin > 10)) { MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11"); tvc.m_low_mixin = true; @@ -2724,6 +2773,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } case rct::RCTTypeSimple: case rct::RCTTypeBulletproof: + case rct::RCTTypeBulletproof2: { // check all this, either reconstructed (so should really pass), or not { @@ -2947,7 +2997,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const uint64_t needed_fee; if (version >= HF_VERSION_PER_BYTE_FEE) { - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version); + const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_block_weights.back() : median, version); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals @@ -2992,7 +3043,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; const uint64_t min_block_weight = get_min_block_weight(version); - std::vector<size_t> weights; + std::vector<uint64_t> weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); weights.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) @@ -3010,7 +3061,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const base_reward = BLOCK_REWARD_OVERESTIMATE; } - uint64_t fee = get_dynamic_base_fee(base_reward, median, version); + const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + uint64_t fee = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_block_weights.back() : median, version); const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); return fee; @@ -3507,7 +3559,8 @@ leave: { try { - new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs); + uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight); + new_height = m_db->add_block(bl, block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs); } catch (const KEY_IMAGE_EXISTS& e) { @@ -3555,25 +3608,114 @@ leave: std::shared_ptr<tools::Notify> block_notify = m_block_notify; if (block_notify) - block_notify->notify(epee::string_tools::pod_to_hex(id).c_str()); + block_notify->notify("%s", epee::string_tools::pod_to_hex(id).c_str(), NULL); return true; } //------------------------------------------------------------------ -bool Blockchain::update_next_cumulative_weight_limit() +void Blockchain::pop_from_long_term_block_weights() { - uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version()); + m_long_term_block_weights.pop_back(); + --m_long_term_block_weights_height; + if (m_long_term_block_weights_height + 1 > m_long_term_block_weights.capacity()) + { + uint64_t block_height = m_long_term_block_weights_height - m_long_term_block_weights.capacity() + 1; + m_long_term_block_weights.push_front(block_height); + } +} +//------------------------------------------------------------------ +uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const +{ + PERF_TIMER(get_next_long_term_block_weight); + + const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), m_db->height()); + CHECK_AND_ASSERT_MES(m_long_term_block_weights.size() == nblocks, 0, + "m_long_term_block_weights is not the expected size"); + + const uint8_t hf_version = get_current_hard_fork_version(); + if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + return block_weight; + + std::vector<uint64_t> weights(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); + uint64_t long_term_median = epee::misc_utils::median(weights); + uint64_t long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + + uint64_t short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5; + uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); + + return long_term_block_weight; +} +//------------------------------------------------------------------ +bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight) +{ + PERF_TIMER(update_next_cumulative_weight_limit); LOG_PRINT_L3("Blockchain::" << __func__); - std::vector<size_t> weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - uint64_t median = epee::misc_utils::median(weights); - m_current_block_cumul_weight_median = median; - if(median <= full_reward_zone) - median = full_reward_zone; + uint64_t local_long_term_effective_median_block_weight; + if (!long_term_effective_median_block_weight) + long_term_effective_median_block_weight = &local_long_term_effective_median_block_weight; + + // when we reach this, the last hf version is not yet written to the db + const uint64_t db_height = m_db->height(); + const uint8_t hf_version = (m_long_term_block_weights_height + 1 < m_db->height()) ? m_db->get_hard_fork_version(db_height - 1) : get_current_hard_fork_version(); + uint64_t full_reward_zone = get_min_block_weight(hf_version); + uint64_t long_term_block_weight; + + // recalc, or reorg ? pop the last one(s) and recompute + uint64_t pop = 0; + if (db_height && db_height <= m_long_term_block_weights_height) + { + pop = m_long_term_block_weights_height - db_height + 1; + while (pop--) + pop_from_long_term_block_weights(); + } + + if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + { + std::vector<uint64_t> weights; + get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + m_current_block_cumul_weight_median = epee::misc_utils::median(weights); + long_term_block_weight = weights.back(); + } + else + { + const uint64_t block_weight = m_db->get_block_weight(db_height - 1); + + std::vector<uint64_t> weights(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); + uint64_t long_term_median = epee::misc_utils::median(weights); + *long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + + uint64_t short_term_constraint = *long_term_effective_median_block_weight + *long_term_effective_median_block_weight * 2 / 5; + long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); + + weights = std::vector<uint64_t>(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); + if (weights.empty()) + weights.resize(1); + weights[0] = long_term_block_weight; + long_term_median = epee::misc_utils::median(weights); + *long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + short_term_constraint = *long_term_effective_median_block_weight + *long_term_effective_median_block_weight * 2 / 5; + + weights.clear(); + get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + + uint64_t short_term_median = epee::misc_utils::median(weights); + uint64_t effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * *long_term_effective_median_block_weight); + + m_current_block_cumul_weight_median = effective_median_block_weight; + } + + m_long_term_block_weights.push_back(long_term_block_weight); + m_long_term_block_weights_height = db_height; + const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), db_height); + CHECK_AND_ASSERT_MES(m_long_term_block_weights.size() == nblocks, false, "Bad m_long_term_block_weights size"); + + if (m_current_block_cumul_weight_median <= full_reward_zone) + m_current_block_cumul_weight_median = full_reward_zone; + + m_current_block_cumul_weight_limit = m_current_block_cumul_weight_median * 2; - m_current_block_cumul_weight_limit = median*2; return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ab66fac8b..7397bd158 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -37,6 +37,7 @@ #include <boost/multi_index/global_fun.hpp> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/member.hpp> +#include <boost/circular_buffer.hpp> #include <atomic> #include <unordered_map> #include <unordered_set> @@ -612,6 +613,13 @@ namespace cryptonote uint64_t get_current_cumulative_block_weight_limit() const; /** + * @brief gets the long term block weight for a new block + * + * @return the long term block weight + */ + uint64_t get_next_long_term_block_weight(uint64_t block_weight) const; + + /** * @brief gets the block weight median based on recent blocks (same window as for the limit) * * @return the median @@ -710,11 +718,18 @@ namespace cryptonote /** * @brief sets a block notify object to call for every new block * - * @param notify the notify object to cal at every new block + * @param notify the notify object to call at every new block */ void set_block_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_block_notify = notify; } /** + * @brief sets a reorg notify object to call for every reorg + * + * @param notify the notify object to call at every reorg + */ + void set_reorg_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_reorg_notify = notify; } + + /** * @brief Put DB in safe sync mode */ void safesyncmode(const bool onoff); @@ -952,7 +967,14 @@ namespace cryptonote */ void on_new_tx_from_block(const cryptonote::transaction &tx); + /** + * @brief returns the timestamps of the last N blocks + */ + std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const; + +#ifndef IN_UNIT_TESTS private: +#endif // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index; @@ -1005,6 +1027,8 @@ namespace cryptonote std::vector<uint64_t> m_timestamps; std::vector<difficulty_type> m_difficulties; uint64_t m_timestamps_and_difficulties_height; + boost::circular_buffer<uint64_t> m_long_term_block_weights; + uint64_t m_long_term_block_weights_height; epee::critical_section m_difficulty_lock; crypto::hash m_difficulty_for_next_block_top_hash; @@ -1042,6 +1066,7 @@ namespace cryptonote bool m_btc_valid; std::shared_ptr<tools::Notify> m_block_notify; + std::shared_ptr<tools::Notify> m_reorg_notify; /** * @brief collects the keys for all outputs being "spent" as an input @@ -1237,7 +1262,7 @@ namespace cryptonote * @param sz return-by-reference the list of weights * @param count the number of blocks to get weights for */ - void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const; + void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const; /** * @brief checks if a transaction is unlocked (its outputs spendable) @@ -1336,9 +1361,11 @@ namespace cryptonote /** * @brief calculate the block weight limit for the next block to be added * + * @param long_term_effective_median_block_weight optionally return that value + * * @return true */ - bool update_next_cumulative_weight_limit(); + bool update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight = NULL); void return_tx_to_pool(std::vector<transaction> &txs); /** @@ -1392,5 +1419,12 @@ namespace cryptonote * At some point, may be used to push an update to miners */ void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie); + + /** + * @brief pops an entry from long term block weights + * + * another is added at the other end if necessary + */ + void pop_from_long_term_block_weights(); }; } // namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2fec6b613..06cefbccc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -173,6 +173,26 @@ namespace cryptonote , "Run a program for each new block, '%s' will be replaced by the block hash" , "" }; + static const command_line::arg_descriptor<std::string> arg_reorg_notify = { + "reorg-notify" + , "Run a program for each reorg, '%s' will be replaced by the split height, " + "'%h' will be replaced by the new blockchain height, '%n' will be " + "replaced by the number of new blocks in the new chain, and '%d' will be " + "replaced by the number of blocks discarded from the old chain" + , "" + }; + static const command_line::arg_descriptor<std::string> arg_block_rate_notify = { + "block-rate-notify" + , "Run a program when the block rate undergoes large fluctuations. This might " + "be a sign of large amounts of hash rate going on and off the Monero network, " + "and thus be of potential interest in predicting attacks. %t will be replaced " + "by the number of minutes for the observation window, %b by the number of " + "blocks observed within that window, and %e by the number of blocks that was " + "expected in that window. It is suggested that this notification is used to " + "automatically increase the number of confirmations required before a payment " + "is acted upon." + , "" + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -283,6 +303,8 @@ namespace cryptonote command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_max_txpool_weight); command_line::add_arg(desc, arg_block_notify); + command_line::add_arg(desc, arg_reorg_notify); + command_line::add_arg(desc, arg_block_rate_notify); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -562,9 +584,30 @@ namespace cryptonote MERROR("Failed to parse block notify spec"); } + try + { + if (!command_line::is_arg_defaulted(vm, arg_reorg_notify)) + m_blockchain_storage.set_reorg_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, arg_reorg_notify).c_str()))); + } + catch (const std::exception &e) + { + MERROR("Failed to parse reorg notify spec"); + } + + try + { + if (!command_line::is_arg_defaulted(vm, arg_block_rate_notify)) + m_block_rate_notify.reset(new tools::Notify(command_line::get_arg(vm, arg_block_rate_notify).c_str())); + } + catch (const std::exception &e) + { + MERROR("Failed to parse block rate notify spec"); + } + const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)}; const cryptonote::test_options regtest_test_options = { - regtest_hard_forks + regtest_hard_forks, + 0 }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); @@ -788,6 +831,7 @@ namespace cryptonote } break; case rct::RCTTypeBulletproof: + case rct::RCTTypeBulletproof2: if (!is_canonical_bulletproof_layout(rv.p.bulletproofs)) { MERROR_VER("Bulletproof does not have canonical form"); @@ -815,7 +859,7 @@ namespace cryptonote { if (!tx_info[n].result) continue; - if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof) + if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2) continue; if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures)) { @@ -1494,6 +1538,7 @@ namespace cryptonote m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); + m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1682,6 +1727,60 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + double factorial(unsigned int n) + { + if (n <= 1) + return 1.0; + double f = n; + while (n-- > 1) + f *= n; + return f; + } + //----------------------------------------------------------------------------------------------- + static double probability(unsigned int blocks, unsigned int expected) + { + // https://www.umass.edu/wsp/resources/poisson/#computing + return pow(expected, blocks) / (factorial(blocks) * exp(expected)); + } + //----------------------------------------------------------------------------------------------- + bool core::check_block_rate() + { + if (m_offline || m_target_blockchain_height > get_current_blockchain_height()) + { + MDEBUG("Not checking block rate, offline or syncing"); + return true; + } + + static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days + + const time_t now = time(NULL); + const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60); + + static const unsigned int seconds[] = { 5400, 3600, 1800, 1200, 600 }; + for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) + { + unsigned int b = 0; + for (time_t ts: timestamps) b += ts >= (time_t)(now - seconds[n]); + const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); + MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); + if (p < threshold) + { + MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); + + std::shared_ptr<tools::Notify> block_rate_notify = m_block_rate_notify; + if (block_rate_notify) + { + auto expected = seconds[n] / DIFFICULTY_TARGET_V2; + block_rate_notify->notify("%t", std::to_string(seconds[n] / 60).c_str(), "%b", std::to_string(b).c_str(), "%e", std::to_string(expected).c_str(), NULL); + } + + break; // no need to look further + } + } + + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 58fe5b7b5..1ab0a47f1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -55,6 +55,7 @@ namespace cryptonote { struct test_options { const std::pair<uint8_t, uint64_t> *hard_forks; + const size_t long_term_block_weight_window; }; extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir; @@ -945,6 +946,13 @@ namespace cryptonote */ bool check_disk_space(); + /** + * @brief checks block rate, and warns if it's too slow + * + * @return true on success, false otherwise + */ + bool check_block_rate(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -969,6 +977,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space + epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? @@ -1005,6 +1014,8 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; + + std::shared_ptr<tools::Notify> m_block_rate_notify; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4bc33b56b..51203e8c3 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -195,7 +195,7 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- - 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, 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, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) + 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, 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, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs) { hw::device &hwdev = sender_account_keys.get_device(); @@ -223,13 +223,15 @@ namespace cryptonote std::vector<tx_extra_field> tx_extra_fields; if (parse_tx_extra(tx.extra, tx_extra_fields)) { + bool add_dummy_payment_id = true; tx_extra_nonce extra_nonce; if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - crypto::hash8 payment_id = null_hash8; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + crypto::hash payment_id = null_hash; + crypto::hash8 payment_id8 = null_hash8; + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) { - LOG_PRINT_L2("Encrypting payment id " << payment_id); + LOG_PRINT_L2("Encrypting payment id " << payment_id8); crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr); if (view_key_pub == null_pkey) { @@ -237,21 +239,53 @@ namespace cryptonote return false; } - if (!hwdev.encrypt_payment_id(payment_id, view_key_pub, tx_key)) + if (!hwdev.encrypt_payment_id(payment_id8, view_key_pub, tx_key)) { LOG_ERROR("Failed to encrypt payment id"); return false; } std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) { LOG_ERROR("Failed to add encrypted payment id to tx extra"); return false; } - LOG_PRINT_L1("Encrypted payment ID: " << payment_id); + LOG_PRINT_L1("Encrypted payment ID: " << payment_id8); + add_dummy_payment_id = false; + } + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + add_dummy_payment_id = false; + } + } + + // we don't add one if we've got more than the usual 1 destination plus change + if (destinations.size() > 2) + add_dummy_payment_id = false; + + if (add_dummy_payment_id) + { + // if we have neither long nor short payment id, add a dummy short one, + // this should end up being the vast majority of txes as time goes on + std::string extra_nonce; + crypto::hash8 payment_id8 = null_hash8; + crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr); + if (view_key_pub == null_pkey) + { + LOG_ERROR("Failed to get key to encrypt dummy payment id with"); + } + else + { + hwdev.encrypt_payment_id(payment_id8, view_key_pub, tx_key); + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); + if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) + { + LOG_ERROR("Failed to add dummy encrypted payment id to tx extra"); + // continue anyway + } } } } @@ -368,49 +402,12 @@ namespace cryptonote for(const tx_destination_entry& dst_entr: destinations) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); - crypto::key_derivation derivation; crypto::public_key out_eph_public_key; - // make additional tx pubkey if necessary - keypair additional_txkey; - if (need_additional_txkeys) - { - additional_txkey.sec = additional_tx_keys[output_index]; - if (dst_entr.is_subaddress) - additional_txkey.pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); - else - additional_txkey.pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(additional_txkey.sec))); - } - - bool r; - if (change_addr && dst_entr.addr == *change_addr) - { - // sending change to yourself; derivation = a*R - r = hwdev.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 = hwdev.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); - } - - if (tx.version > 1) - { - crypto::secret_key scalar1; - hwdev.derivation_to_scalar(derivation, output_index, scalar1); - amount_keys.push_back(rct::sk2rct(scalar1)); - } - r = hwdev.derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); - - hwdev.add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, output_index, amount_keys.back(), out_eph_public_key); + hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key, + dst_entr, change_addr, output_index, + need_additional_txkeys, additional_tx_keys, + additional_tx_public_keys, amount_keys, out_eph_public_key); tx_out out; out.amount = dst_entr.amount; @@ -491,7 +488,7 @@ namespace cryptonote // the non-simple version is slightly smaller, but assumes all real inputs // are on the same index, so can only be used if there just one ring. - bool use_simple_rct = sources.size() > 1 || range_proof_type != rct::RangeProofBorromean; + bool use_simple_rct = sources.size() > 1 || rct_config.range_proof_type != rct::RangeProofBorromean; if (!use_simple_rct) { @@ -589,9 +586,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, msout ? &kLRki : NULL, msout, index, outSk, range_proof_type, hwdev); + 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, rct_config, hwdev); else - 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, hwdev); // 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, rct_config, hwdev); // same index assumption memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); @@ -604,7 +601,7 @@ 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, 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, rct::RangeProofType range_proof_type, rct::multisig_out *msout) + 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, 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, const rct::RCTConfig &rct_config, rct::multisig_out *msout) { hw::device &hwdev = sender_account_keys.get_device(); hwdev.open_tx(tx_key); @@ -622,7 +619,7 @@ namespace cryptonote additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec); } - bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, range_proof_type, msout); + bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout); hwdev.close_tx(); return r; } @@ -634,7 +631,7 @@ namespace cryptonote crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; std::vector<tx_destination_entry> destinations_copy = destinations; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, NULL); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index f2cf7b6ca..1b2d8d347 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -73,25 +73,36 @@ namespace cryptonote struct tx_destination_entry { + std::string original; uint64_t amount; //money account_public_address addr; //destination address bool is_subaddress; + bool is_integrated; - tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false) { } - tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress) { } + tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false), is_integrated(false) { } + tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { } + tx_destination_entry(const std::string &o, uint64_t a, const account_public_address &ad, bool is_subaddress) : original(o), amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { } BEGIN_SERIALIZE_OBJECT() + FIELD(original) VARINT_FIELD(amount) FIELD(addr) FIELD(is_subaddress) + FIELD(is_integrated) END_SERIALIZE() }; //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr); 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, 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, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = 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, 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, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); + 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, 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, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = 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, 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, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL); + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) ; bool generate_genesis_block( block& bl @@ -102,7 +113,7 @@ namespace cryptonote } BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1) -BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1) +BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 2) namespace boost { @@ -132,6 +143,13 @@ namespace boost if (ver < 1) return; a & x.is_subaddress; + if (ver < 2) + { + x.is_integrated = false; + return; + } + a & x.original; + a & x.is_integrated; } } } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 6464d372f..3348989f2 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -77,6 +77,7 @@ namespace { << "POW hash: " << header.pow_hash << std::endl << "block size: " << header.block_size << std::endl << "block weight: " << header.block_weight << std::endl + << "long term weight: " << header.long_term_weight << std::endl << "num txes: " << header.num_txes << std::endl << "reward: " << cryptonote::print_money(header.reward); } diff --git a/src/device/device.hpp b/src/device/device.hpp index cb9117650..c0cdcd753 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -68,6 +68,7 @@ namespace cryptonote struct account_public_address; struct account_keys; struct subaddress_index; + struct tx_destination_entry; } namespace hw { @@ -188,12 +189,15 @@ namespace hw { return encrypt_payment_id(payment_id, public_key, secret_key); } - virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) = 0; - virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) = 0; - - virtual bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) = 0; + virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) = 0; + virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) = 0; + virtual bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) = 0; virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0; virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0; diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index a4f40e041..c6cd7c06b 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -34,8 +34,10 @@ #include "common/int-util.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "ringct/rctOps.h" +#include "log.hpp" #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d #define CHACHA8_KEY_TAIL 0x8c @@ -278,10 +280,55 @@ namespace hw { return true; } + bool device_default::generate_output_ephemeral_keys(const size_t tx_version, + const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key) { - bool device_default::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { - return true; + crypto::key_derivation derivation; + + // make additional tx pubkey if necessary + cryptonote::keypair additional_txkey; + if (need_additional_txkeys) + { + 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 + additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec))); + } + + bool r; + if (change_addr && dst_entr.addr == *change_addr) + { + // sending change to yourself; derivation = a*R + r = 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 = 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); + } + + if (tx_version > 1) + { + crypto::secret_key scalar1; + derivation_to_scalar(derivation, output_index, scalar1); + amount_keys.push_back(rct::sk2rct(scalar1)); + } + r = derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + + return r; } bool device_default::encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { @@ -302,13 +349,13 @@ namespace hw { return true; } - bool device_default::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) { - rct::ecdhEncode(unmasked, sharedSec); + bool device_default::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) { + rct::ecdhEncode(unmasked, sharedSec, short_amount); return true; } - bool device_default::ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) { - rct::ecdhDecode(masked, sharedSec); + bool device_default::ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) { + rct::ecdhDecode(masked, sharedSec, short_amount); return true; } diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 5c59a9066..04b9b4234 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -111,12 +111,15 @@ namespace hw { bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; - bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) override; - bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) override; - - bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; - + bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override; + bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override; + + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index a17784960..f73b1d5a7 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -32,6 +32,7 @@ #include "ringct/rctOps.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include <boost/thread/locks.hpp> #include <boost/thread/lock_guard.hpp> @@ -67,10 +68,12 @@ namespace hw { /* === Keymap ==== */ /* ===================================================================== */ - ABPkeys::ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, const size_t real_output_index, const rct::key& P, const rct::key& AK) { + ABPkeys::ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, const bool is_change, const bool need_additional_txkeys, const size_t real_output_index, const rct::key& P, const rct::key& AK) { Aout = A; Bout = B; is_subaddress = is_subaddr; + is_change_address = is_change; + additional_key = need_additional_txkeys; index = real_output_index; Pout = P; AKout = AK; @@ -80,6 +83,8 @@ namespace hw { Aout = keys.Aout; Bout = keys.Bout; is_subaddress = keys.is_subaddress; + is_change_address = keys.is_change_address; + additional_key = keys.additional_key; index = keys.index; Pout = keys.Pout; AKout = keys.AKout; @@ -137,6 +142,8 @@ namespace hw { static int device_id = 0; + #define PROTOCOL_VERSION 2 + #define INS_NONE 0x00 #define INS_RESET 0x02 @@ -168,6 +175,7 @@ namespace hw { #define INS_STEALTH 0x76 #define INS_BLIND 0x78 #define INS_UNBLIND 0x7A + #define INS_GEN_TXOUT_KEYS 0x7B #define INS_VALIDATE 0x7C #define INS_MLSAG 0x7E #define INS_CLOSE_TX 0x80 @@ -265,7 +273,7 @@ namespace hw { int device_ledger::set_command_header(unsigned char ins, unsigned char p1, unsigned char p2) { reset_buffer(); int offset = 0; - this->buffer_send[0] = 0x00; + this->buffer_send[0] = PROTOCOL_VERSION; this->buffer_send[1] = ins; this->buffer_send[2] = p1; this->buffer_send[3] = p2; @@ -481,11 +489,11 @@ namespace hw { } const std::size_t output_index_x = output_index; crypto::public_key derived_pub_x; - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32); - hw::ledger::log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x)); + log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32); + log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x)); this->controle_device->derive_subaddress_public_key(pub_x, derivation_x,output_index_x,derived_pub_x); - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32); + log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32); #endif if ((this->mode == TRANSACTION_PARSE) && has_view_key) { @@ -531,11 +539,11 @@ namespace hw { const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); const cryptonote::subaddress_index index_x = index; crypto::public_key D_x; - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data,32); - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_spend_secret_key", keys_x.m_spend_secret_key.data,32); - hw::ledger::log_message ("get_subaddress_spend_public_key: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); + log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data,32); + log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_spend_secret_key", keys_x.m_spend_secret_key.data,32); + log_message ("get_subaddress_spend_public_key: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); D_x = this->controle_device->get_subaddress_spend_public_key(keys_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[OUT]] derivation ", D_x.data, 32); + log_hexbuffer("get_subaddress_spend_public_key: [[OUT]] derivation ", D_x.data, 32); #endif if (index.is_zero()) { @@ -582,14 +590,14 @@ namespace hw { const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); const cryptonote::subaddress_index index_x = index; cryptonote::account_public_address address_x; - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_public_key", keys_x.m_account_address.m_view_public_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_spend_public_key", keys_x.m_account_address.m_spend_public_key.data, 32); - hw::ledger::log_message ("get_subaddress: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_public_key", keys_x.m_account_address.m_view_public_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_spend_public_key", keys_x.m_account_address.m_spend_public_key.data, 32); + log_message ("get_subaddress: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); address_x = this->controle_device->get_subaddress(keys_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress: [[OUT]] keys.m_view_public_key ", address_x.m_view_public_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[OUT]] keys.m_spend_public_key", address_x.m_spend_public_key.data, 32); + log_hexbuffer("get_subaddress: [[OUT]] keys.m_view_public_key ", address_x.m_view_public_key.data, 32); + log_hexbuffer("get_subaddress: [[OUT]] keys.m_spend_public_key", address_x.m_spend_public_key.data, 32); #endif if (index.is_zero()) { @@ -625,10 +633,10 @@ namespace hw { const crypto::secret_key sec_x = hw::ledger::decrypt(sec); const cryptonote::subaddress_index index_x = index; crypto::secret_key sub_sec_x; - hw::ledger::log_message ("get_subaddress_secret_key: [[IN]] index ", std::to_string(index.major)+"."+std::to_string(index.minor)); - hw::ledger::log_hexbuffer("get_subaddress_secret_key: [[IN]] sec ", sec_x.data, 32); + log_message ("get_subaddress_secret_key: [[IN]] index ", std::to_string(index.major)+"."+std::to_string(index.minor)); + log_hexbuffer("get_subaddress_secret_key: [[IN]] sec ", sec_x.data, 32); sub_sec_x = this->controle_device->get_subaddress_secret_key(sec_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress_secret_key: [[OUT]] sub_sec", sub_sec_x.data, 32); + log_hexbuffer("get_subaddress_secret_key: [[OUT]] sub_sec", sub_sec_x.data, 32); #endif int offset = set_command_header_noopt(INS_GET_SUBADDRESS_SECRET_KEY); @@ -690,10 +698,10 @@ namespace hw { const rct::key P_x = P; const rct::key a_x = hw::ledger::decrypt(a); rct::key aP_x; - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] P ", (char*)P_x.bytes, 32); - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] P ", (char*)P_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); this->controle_device->scalarmultKey(aP_x, P_x, a_x); - hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aP", (char*)aP_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[OUT]] aP", (char*)aP_x.bytes, 32); #endif int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_KEY); @@ -725,9 +733,9 @@ namespace hw { #ifdef DEBUG_HWDEVICE const rct::key a_x = hw::ledger::decrypt(a); rct::key aG_x; - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); this->controle_device->scalarmultBase(aG_x, a_x); - hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aG", (char*)aG_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[OUT]] aG", (char*)aG_x.bytes, 32); #endif int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_BASE); @@ -820,10 +828,10 @@ namespace hw { const crypto::public_key pub_x = pub; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::key_derivation derivation_x; - hw::ledger::log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32); this->controle_device->generate_key_derivation(pub_x, sec_x, derivation_x); - hw::ledger::log_hexbuffer("generate_key_derivation: [[OUT]] derivation", derivation_x.data, 32); + log_hexbuffer("generate_key_derivation: [[OUT]] derivation", derivation_x.data, 32); #endif if ((this->mode == TRANSACTION_PARSE) && has_view_key) { @@ -889,10 +897,10 @@ namespace hw { const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); const size_t output_index_x = output_index; crypto::ec_scalar res_x; - hw::ledger::log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derivation_to_scalar: [[IN]] output_index ", std::to_string(output_index_x)); + log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derivation_to_scalar: [[IN]] output_index ", std::to_string(output_index_x)); this->controle_device->derivation_to_scalar(derivation_x, output_index_x, res_x); - hw::ledger::log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); + log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVATION_TO_SCALAR); @@ -929,11 +937,11 @@ namespace hw { const std::size_t output_index_x = output_index; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::secret_key derived_sec_x; - hw::ledger::log_hexbuffer("derive_secret_key: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derive_secret_key: [[IN]] index ", std::to_string(output_index_x)); - hw::ledger::log_hexbuffer("derive_secret_key: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("derive_secret_key: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_secret_key: [[IN]] index ", std::to_string(output_index_x)); + log_hexbuffer("derive_secret_key: [[IN]] sec ", sec_x.data, 32); this->controle_device->derive_secret_key(derivation_x, output_index_x, sec_x, derived_sec_x); - hw::ledger::log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); + log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVE_SECRET_KEY); @@ -973,11 +981,11 @@ namespace hw { const std::size_t output_index_x = output_index; const crypto::public_key pub_x = pub; crypto::public_key derived_pub_x; - hw::ledger::log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x)); - hw::ledger::log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x)); + log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32); this->controle_device->derive_public_key(derivation_x, output_index_x, pub_x, derived_pub_x); - hw::ledger::log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); + log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVE_PUBLIC_KEY); @@ -1014,11 +1022,11 @@ namespace hw { #ifdef DEBUG_HWDEVICE const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::public_key pub_x; - hw::ledger::log_hexbuffer("secret_key_to_public_key: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("secret_key_to_public_key: [[IN]] sec ", sec_x.data, 32); bool rc = this->controle_device->secret_key_to_public_key(sec_x, pub_x); - hw::ledger::log_hexbuffer("secret_key_to_public_key: [[OUT]] pub", pub_x.data, 32); + log_hexbuffer("secret_key_to_public_key: [[OUT]] pub", pub_x.data, 32); if (!rc){ - hw::ledger::log_message("secret_key_to_public_key", "secret_key rejected"); + log_message("secret_key_to_public_key", "secret_key rejected"); } #endif @@ -1048,10 +1056,10 @@ namespace hw { const crypto::public_key pub_x = pub; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::key_image image_x; - hw::ledger::log_hexbuffer("generate_key_image: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("generate_key_image: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("generate_key_image: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("generate_key_image: [[IN]] sec ", sec_x.data, 32); this->controle_device->generate_key_image(pub_x, sec_x, image_x); - hw::ledger::log_hexbuffer("generate_key_image: [[OUT]] image ", image_x.data, 32); + log_hexbuffer("generate_key_image: [[OUT]] image ", image_x.data, 32); #endif int offset = set_command_header_noopt(INS_GEN_KEY_IMAGE); @@ -1135,23 +1143,155 @@ namespace hw { return true; } - bool device_ledger::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { - AUTO_LOCK_CMD(); - key_map.add(ABPkeys(rct::pk2rct(Aout),rct::pk2rct(Bout), is_subaddress, real_output_index, rct::pk2rct(out_eph_public_key), amount_key)); + + bool device_ledger::generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) { + AUTO_LOCK_CMD(); + + #ifdef DEBUG_HWDEVICE + const size_t &tx_version_x = tx_version; + const cryptonote::account_keys sender_account_keys_x = sender_account_keys; + memmove((void*)sender_account_keys_x.m_view_secret_key.data, dbg_viewkey.data, 32); + + const crypto::public_key &txkey_pub_x = txkey_pub; + const crypto::secret_key &tx_key_x = tx_key; + const cryptonote::tx_destination_entry &dst_entr_x = dst_entr; + const boost::optional<cryptonote::account_public_address> &change_addr_x = change_addr; + const size_t &output_index_x = output_index; + const bool &need_additional_txkeys_x = need_additional_txkeys; + const std::vector<crypto::secret_key> &additional_tx_keys_x = additional_tx_keys; + std::vector<crypto::public_key> additional_tx_public_keys_x; + std::vector<rct::key> amount_keys_x; + crypto::public_key out_eph_public_key_x; + this->controle_device->generate_output_ephemeral_keys(tx_version_x, sender_account_keys_x, txkey_pub_x, tx_key_x, dst_entr_x, change_addr_x, output_index_x, need_additional_txkeys_x, additional_tx_keys_x, + additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x); + #endif + + // make additional tx pubkey if necessary + cryptonote::keypair additional_txkey; + if (need_additional_txkeys) { + additional_txkey.sec = additional_tx_keys[output_index]; + } + + //compute derivation, out_eph_public_key, and amount key in one shot on device, to ensure checkable link + const crypto::secret_key *sec; + bool is_change; + + if (change_addr && dst_entr.addr == *change_addr) + { + // sending change to yourself; derivation = a*R + is_change = true; + sec = &sender_account_keys.m_view_secret_key; + } + else + { + is_change = false; + if (dst_entr.is_subaddress && need_additional_txkeys) { + sec = &additional_txkey.sec; + } else { + sec = &tx_key; + } + } + + int offset = set_command_header_noopt(INS_GEN_TXOUT_KEYS); + //tx_version + this->buffer_send[offset+0] = tx_version>>24; + this->buffer_send[offset+1] = tx_version>>16; + this->buffer_send[offset+2] = tx_version>>8; + this->buffer_send[offset+3] = tx_version>>0; + offset += 4; + //tx_sec + memmove(&this->buffer_send[offset], sec->data, 32); + offset += 32; + //Aout + memmove(&this->buffer_send[offset], dst_entr.addr.m_view_public_key.data, 32); + offset += 32; + //Bout + memmove(&this->buffer_send[offset], dst_entr.addr.m_spend_public_key.data, 32); + offset += 32; + //output index + this->buffer_send[offset+0] = output_index>>24; + this->buffer_send[offset+1] = output_index>>16; + this->buffer_send[offset+2] = output_index>>8; + this->buffer_send[offset+3] = output_index>>0; + offset += 4; + //is_change, + this->buffer_send[offset] = is_change; + offset++; + //is_subaddress + this->buffer_send[offset] = dst_entr.is_subaddress; + offset++; + //need_additional_key + this->buffer_send[offset] = need_additional_txkeys; + offset++; + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + offset = 0; + unsigned int recv_len = this->length_recv; + if (need_additional_txkeys) + { + ASSERT_X(recv_len>=32, "Not enought data from device"); + memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); + additional_tx_public_keys.push_back(additional_txkey.pub); + offset += 32; + recv_len -= 32; + } + if (tx_version > 1) + { + ASSERT_X(recv_len>=32, "Not enought data from device"); + crypto::secret_key scalar1; + memmove(scalar1.data, &this->buffer_recv[offset],32); + amount_keys.push_back(rct::sk2rct(scalar1)); + offset += 32; + recv_len -= 32; + } + ASSERT_X(recv_len>=32, "Not enought data from device"); + memmove(out_eph_public_key.data, &this->buffer_recv[offset], 32); + recv_len -= 32; + + // add ABPkeys + this->add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, is_change, + need_additional_txkeys, output_index, + amount_keys.back(), out_eph_public_key); + + #ifdef DEBUG_HWDEVICE + hw::ledger::check32("generate_output_ephemeral_keys", "amount_key", (const char*)amount_keys_x.back().bytes, (const char*)hw::ledger::decrypt(amount_keys.back()).bytes); + if (need_additional_txkeys) { + hw::ledger::check32("generate_output_ephemeral_keys", "additional_tx_key", additional_tx_keys_x.back().data, additional_tx_keys.back().data); + } + hw::ledger::check32("generate_output_ephemeral_keys", "out_eph_public_key", out_eph_public_key_x.data, out_eph_public_key.data); + #endif + + return true; + } + + bool device_ledger::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change, + const bool need_additional, const size_t real_output_index, + const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { + key_map.add(ABPkeys(rct::pk2rct(Aout),rct::pk2rct(Bout), is_subaddress, is_change, need_additional, real_output_index, rct::pk2rct(out_eph_public_key), amount_key)); return true; } - bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout) { + bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout, bool short_amount) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE const rct::key AKout_x = hw::ledger::decrypt(AKout); rct::ecdhTuple unmasked_x = unmasked; - this->controle_device->ecdhEncode(unmasked_x, AKout_x); + this->controle_device->ecdhEncode(unmasked_x, AKout_x, short_amount); #endif - int offset = set_command_header_noopt(INS_BLIND); + int offset = set_command_header(INS_BLIND); + //options + this->buffer_send[offset] = short_amount?0x02:0x00; + offset += 1; // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1173,23 +1313,25 @@ namespace hw { hw::ledger::check32("ecdhEncode", "amount", (char*)unmasked_x.amount.bytes, (char*)unmasked.amount.bytes); hw::ledger::check32("ecdhEncode", "mask", (char*)unmasked_x.mask.bytes, (char*)unmasked.mask.bytes); - hw::ledger::log_hexbuffer("Blind AKV input", (char*)&this->buffer_recv[64], 3*32); + log_hexbuffer("Blind AKV input", (char*)&this->buffer_recv[64], 3*32); #endif return true; } - bool device_ledger::ecdhDecode(rct::ecdhTuple & masked, const rct::key & AKout) { + bool device_ledger::ecdhDecode(rct::ecdhTuple & masked, const rct::key & AKout, bool short_amount) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE const rct::key AKout_x = hw::ledger::decrypt(AKout); rct::ecdhTuple masked_x = masked; - this->controle_device->ecdhDecode(masked_x, AKout_x); + this->controle_device->ecdhDecode(masked_x, AKout_x, short_amount); #endif - int offset = set_command_header_noopt(INS_UNBLIND); - + int offset = set_command_header(INS_UNBLIND); + //options + this->buffer_send[offset] = short_amount?0x02:0x00; + offset += 1; // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1284,7 +1426,11 @@ namespace hw { // ====== Aout, Bout, AKout, C, v, k ====== kv_offset = data_offset; - C_offset = kv_offset+ (32*2)*outputs_size; + if (type==rct::RCTTypeBulletproof2) { + C_offset = kv_offset+ (8)*outputs_size; + } else { + C_offset = kv_offset+ (32+32)*outputs_size; + } for ( i = 0; i < outputs_size; i++) { ABPkeys outKeys; bool found; @@ -1297,11 +1443,15 @@ namespace hw { offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; + this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2)?0x02:0x00; offset += 1; if (found) { //is_subaddress this->buffer_send[offset] = outKeys.is_subaddress; offset++; + //is_change_address + this->buffer_send[offset] = outKeys.is_change_address; + offset++; //Aout memmove(this->buffer_send+offset, outKeys.Aout.bytes, 32); offset+=32; @@ -1313,26 +1463,37 @@ namespace hw { offset+=32; } else { // dummy: is_subaddress Aout Bout AKout - offset += 1+32*3; + offset += 2+32*3; } //C memmove(this->buffer_send+offset, data+C_offset,32); offset += 32; C_offset += 32; - //k - memmove(this->buffer_send+offset, data+kv_offset,32); - offset += 32; - //v - kv_offset += 32; - memmove(this->buffer_send+offset, data+kv_offset,32); - offset += 32; - kv_offset += 32; + if (type==rct::RCTTypeBulletproof2) { + //k + memset(this->buffer_send+offset, 0, 32); + offset += 32; + //v + memset(this->buffer_send+offset, 0, 32); + memmove(this->buffer_send+offset, data+kv_offset,8); + offset += 32; + kv_offset += 8; + } else { + //k + memmove(this->buffer_send+offset, data+kv_offset,32); + offset += 32; + kv_offset += 32; + //v + memmove(this->buffer_send+offset, data+kv_offset,32); + offset += 32; + kv_offset += 32; + } this->buffer_send[4] = offset-5; this->length_send = offset; this->exchange(); #ifdef DEBUG_HWDEVICE - hw::ledger::log_hexbuffer("Prehash AKV input", (char*)&this->buffer_recv[64], 3*32); + log_hexbuffer("Prehash AKV input", (char*)&this->buffer_recv[64], 3*32); #endif } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index dde69fbfd..2f2ec7089 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -56,12 +56,14 @@ namespace hw { rct::key Aout; rct::key Bout; bool is_subaddress; + bool is_change_address; + bool additional_key ; size_t index; rct::key Pout; rct::key AKout; - ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, size_t index, const rct::key& P,const rct::key& AK); + ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, bool is_subaddress, bool is_change_address, size_t index, const rct::key& P,const rct::key& AK); ABPkeys(const ABPkeys& keys) ; - ABPkeys() {index=0;is_subaddress=false;} + ABPkeys() {index=0;is_subaddress=false;is_subaddress=false;is_change_address=false;} }; class Keymap { @@ -105,7 +107,9 @@ namespace hw { device_mode mode; // map public destination key to ephemeral destination key Keymap key_map; - + bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change, + const bool need_additional, const size_t real_output_index, + const rct::key &amount_key, const crypto::public_key &out_eph_public_key); // To speed up blockchain parsing the view key maybe handle here. crypto::secret_key viewkey; bool has_view_key; @@ -190,12 +194,15 @@ namespace hw { bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; - bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) override; - bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) override; - - bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; + bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_format) override; + bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_format) override; + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/device/log.cpp b/src/device/log.cpp index c9d3b551b..87505798b 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -66,7 +66,7 @@ namespace hw { void decrypt(char* buf, size_t len) { - #ifdef IODUMMYCRYPT_HWDEVICE + #if defined(IODUMMYCRYPT_HWDEVICE) || defined(IONOCRYPT_HWDEVICE) size_t i; if (len == 32) { //view key? @@ -86,11 +86,13 @@ namespace hw { return; } } + #if defined(IODUMMYCRYPT_HWDEVICE) //std decrypt: XOR.55h for (i = 0; i<len;i++) { buf[i] ^= 0x55; } #endif + #endif } crypto::key_derivation decrypt(const crypto::key_derivation &derivation) { diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 41bbf6ca3..747b686cd 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -487,18 +487,58 @@ namespace rct { //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a // where C= aG + bH - void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec) { - key sharedSec1 = hash_to_scalar(sharedSec); - key sharedSec2 = hash_to_scalar(sharedSec1); + static key ecdhHash(const key &k) + { + char data[38]; + rct::key hash; + memcpy(data, "amount", 6); + memcpy(data + 6, &k, sizeof(k)); + cn_fast_hash(hash, data, sizeof(data)); + return hash; + } + static void xor8(key &v, const key &k) + { + for (int i = 0; i < 8; ++i) + v.bytes[i] ^= k.bytes[i]; + } + key genCommitmentMask(const key &sk) + { + char data[15 + sizeof(key)]; + memcpy(data, "commitment_mask", 15); + memcpy(data + 15, &sk, sizeof(sk)); + key scalar; + hash_to_scalar(scalar, data, sizeof(data)); + return scalar; + } + + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool v2) { //encode - sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); - sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + if (v2) + { + unmasked.mask = zero(); + xor8(unmasked.amount, ecdhHash(sharedSec)); + } + else + { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); + sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + } } - void ecdhDecode(ecdhTuple & masked, const key & sharedSec) { - key sharedSec1 = hash_to_scalar(sharedSec); - key sharedSec2 = hash_to_scalar(sharedSec1); + void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2) { //decode - sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); - sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); + if (v2) + { + masked.mask = genCommitmentMask(sharedSec); + xor8(masked.amount, ecdhHash(sharedSec)); + } + else + { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); + sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); + } } } diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 60e920b3a..dd6d87593 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -182,7 +182,8 @@ namespace rct { //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a // where C= aG + bH - void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec); - void ecdhDecode(ecdhTuple & masked, const key & sharedSec); + key genCommitmentMask(const key &sk); + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool v2); + void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2); } #endif /* RCTOPS_H */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 0d1789a38..a9f6d6a53 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -45,18 +45,12 @@ using namespace std; #define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}} namespace rct { - Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) + Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, const std::vector<key> &sk) { - mask = rct::skGen(); - Bulletproof proof = bulletproof_PROVE(amount, mask); - CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); - C = proof.V[0]; - return proof; - } - - Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts) - { - masks = rct::skvGen(amounts.size()); + CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes"); + masks.resize(amounts.size()); + for (size_t i = 0; i < masks.size(); ++i) + masks[i] = genCommitmentMask(sk[i]); Bulletproof proof = bulletproof_PROVE(amounts, masks); CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); C = proof.V; @@ -391,7 +385,7 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; - if (rv.type == RCTTypeBulletproof) + if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2) { kv.reserve((6*2+9) * rv.p.bulletproofs.size()); for (const auto &p: rv.p.bulletproofs) @@ -652,7 +646,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, hw::device &hwdev) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -682,7 +676,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2); } //set txn fee @@ -703,18 +697,18 @@ namespace rct { return rv; } - rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev) { + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev) { unsigned int index; ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, hwdev); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev); } //RCT simple //for post-rct only - rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) { - const bool bulletproof = range_proof_type != RangeProofBorromean; + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { + const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean; CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -730,7 +724,7 @@ namespace rct { } rctSig rv; - rv.type = bulletproof ? RCTTypeBulletproof : RCTTypeSimple; + rv.type = bulletproof ? (rct_config.bp_version == 0 || rct_config.bp_version >= 2 ? RCTTypeBulletproof2 : RCTTypeBulletproof) : RCTTypeSimple; rv.message = message; rv.outPk.resize(destinations.size()); if (!bulletproof) @@ -759,10 +753,11 @@ namespace rct { std::vector<uint64_t> proof_amounts; size_t n_amounts = outamounts.size(); size_t amounts_proved = 0; - if (range_proof_type == RangeProofPaddedBulletproof) + if (rct_config.range_proof_type == RangeProofPaddedBulletproof) { rct::keyV C, masks; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts)); + const std::vector<key> keys(amount_keys.begin(), amount_keys.end()); + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif @@ -775,14 +770,17 @@ namespace rct { else while (amounts_proved < n_amounts) { size_t batch_size = 1; - if (range_proof_type == RangeProofMultiOutputBulletproof) + if (rct_config.range_proof_type == RangeProofMultiOutputBulletproof) while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) batch_size *= 2; rct::keyV C, masks; std::vector<uint64_t> batch_amounts(batch_size); for (i = 0; i < batch_size; ++i) batch_amounts[i] = outamounts[i + amounts_proved]; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + std::vector<key> keys(batch_size); + for (size_t j = 0; j < batch_size; ++j) + keys[j] = amount_keys[amounts_proved + j]; + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif @@ -803,7 +801,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(outamounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2); } //set txn fee @@ -835,7 +833,7 @@ namespace rct { return rv; } - rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev) { + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev) { std::vector<unsigned int> index; index.resize(inPk.size()); ctkeyM mixRing; @@ -845,7 +843,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev); } //RingCT protocol @@ -935,7 +933,8 @@ namespace rct { { CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); const rctSig &rv = *rvp; - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "verRctSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, + false, "verRctSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); if (bulletproof) { @@ -1034,7 +1033,8 @@ namespace rct { { PERF_TIMER(verRctNonSemanticsSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, + false, "verRctNonSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); // semantics check is early, and mixRing/MGs aren't resolved yet if (bulletproof) @@ -1100,7 +1100,7 @@ namespace rct { //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - hwdev.ecdhDecode(ecdh_info, sk); + hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -1124,13 +1124,13 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - hwdev.ecdhDecode(ecdh_info, sk); + hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -1154,7 +1154,7 @@ namespace rct { } bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "unsupported rct type"); CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size"); diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index ae8bb91d7..10a8686ee 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -119,10 +119,10 @@ namespace rct { //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, hw::device &hwdev); - rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSemanticsSimple(const rctSig & rv); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 90ed65df0..f01e683cb 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -217,6 +217,7 @@ namespace rct { { case RCTTypeSimple: case RCTTypeBulletproof: + case RCTTypeBulletproof2: return true; default: return false; @@ -228,6 +229,7 @@ namespace rct { switch (type) { case RCTTypeBulletproof: + case RCTTypeBulletproof2: return true; default: return false; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 18290637b..c6c21ad22 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -128,7 +128,7 @@ namespace rct { key senderPk; BEGIN_SERIALIZE_OBJECT() - FIELD(mask) + FIELD(mask) // not saved from v2 BPs FIELD(amount) // FIELD(senderPk) // not serialized, as we do not use it in monero currently END_SERIALIZE() @@ -230,8 +230,13 @@ namespace rct { RCTTypeFull = 1, RCTTypeSimple = 2, RCTTypeBulletproof = 3, + RCTTypeBulletproof2 = 4, }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; + struct RCTConfig { + RangeProofType range_proof_type; + int bp_version; + }; struct rctSigBase { uint8_t type; key message; @@ -248,7 +253,7 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) return false; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help @@ -277,7 +282,19 @@ namespace rct { return false; for (size_t i = 0; i < outputs; ++i) { - FIELDS(ecdhInfo[i]) + if (type == RCTTypeBulletproof2) + { + ar.begin_object(); + if (!typename Archive<W>::is_saving()) + memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes)); + crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount; + FIELD(amount); + ar.end_object(); + } + else + { + FIELDS(ecdhInfo[i]) + } if (outputs - i > 1) ar.delimit_array(); } @@ -309,12 +326,15 @@ namespace rct { { if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) return false; - if (type == RCTTypeBulletproof) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) { uint32_t nbp = bulletproofs.size(); - FIELD(nbp) + if (type == RCTTypeBulletproof2) + VARINT_FIELD(nbp) + else + FIELD(nbp) ar.tag("bp"); ar.begin_array(); if (nbp > outputs) @@ -350,7 +370,7 @@ namespace rct { ar.begin_array(); // we keep a byte for size of MGs, because we don't know whether this is // a simple or full rct signature, and it's starting to annoy the hell out of me - size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof) ? inputs : 1; + size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); if (MGs.size() != mg_elements) return false; @@ -368,7 +388,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { ar.begin_array(); - size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof) ? 1 : inputs) + 1; + size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; @@ -394,7 +414,7 @@ namespace rct { ar.delimit_array(); } ar.end_array(); - if (type == RCTTypeBulletproof) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -418,12 +438,12 @@ namespace rct { keyV& get_pseudo_outs() { - return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; } }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index bc33dc77f..8098b62ad 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1264,6 +1264,7 @@ namespace cryptonote response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; + response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height); return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 3b654d4cb..8ac1279a2 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1128,6 +1128,7 @@ namespace cryptonote uint64_t block_weight; uint64_t num_txes; std::string pow_hash; + uint64_t long_term_weight; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(major_version) @@ -1146,6 +1147,7 @@ namespace cryptonote KV_SERIALIZE_OPT(block_weight, (uint64_t)0) KV_SERIALIZE(num_txes) KV_SERIALIZE(pow_hash) + KV_SERIALIZE_OPT(long_term_weight, (uint64_t)0) END_KV_SERIALIZE_MAP() }; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 18b596662..4a4754471 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -107,6 +107,14 @@ typedef cryptonote::simple_wallet sw; if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); +#define LONG_PAYMENT_ID_SUPPORT_CHECK() \ + do { \ + if (!m_long_payment_id_support) { \ + fail_msg_writer() << tr("Long payment IDs are obsolete. Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ + return true; \ + } \ + } while(0) + enum TransferType { Transfer, TransferLocked, @@ -134,6 +142,7 @@ namespace const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; + const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support", sw::tr("Support obsolete long (unencrypted) payment ids"), false}; const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; @@ -766,6 +775,8 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); + crypto::hash payment_id; if (args.size() > 0) { @@ -1986,6 +1997,8 @@ bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = st bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); + const auto pwd_container = get_and_verify_password(); if (pwd_container) { @@ -2308,33 +2321,33 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show the blockchain height.")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), - tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]"), + tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id (obsolete)>]"), tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), - tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>]"), + tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]"), tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_sweep_all", boost::bind(&simple_wallet::locked_sweep_all, this, _1), - tr("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id>]"), + tr("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]"), tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), - tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]"), + tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"), tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), - tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"), + tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]"), tr("Send all unlocked outputs below the threshold to an address.")); m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), - tr("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]"), + tr("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]"), tr("Send a single output of the given key image to an address without change.")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), - tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"), + tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]"), tr("Donate <amount> to the development team (donate.getmonero.org).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), @@ -2550,7 +2563,7 @@ simple_wallet::simple_wallet() tr("Change the wallet's password.")); m_cmd_binder.set_handler("payment_id", boost::bind(&simple_wallet::payment_id, this, _1), - tr("Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.")); + tr("Generate a new random full size payment id (obsolete). These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.")); m_cmd_binder.set_handler("fee", boost::bind(&simple_wallet::print_fee_info, this, _1), tr("Print the information about the current fee and transaction backlog.")); @@ -3480,6 +3493,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead); m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names); + m_long_payment_id_support = command_line::get_arg(vm, arg_long_payment_id_support); m_restoring = !m_generate_from_view_key.empty() || !m_generate_from_spend_key.empty() || !m_generate_from_keys.empty() || @@ -4173,14 +4187,10 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tx_extra_nonce extra_nonce; if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - crypto::hash8 payment_id8 = crypto::null_hash8; crypto::hash payment_id = crypto::null_hash; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) - message_writer() << - tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); - else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) message_writer(console_color_red, false) << - tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead"); + (m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead.")); } } if (m_auto_refresh_refreshing) @@ -4781,6 +4791,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri bool r = true; if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id)) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); + std::string extra_nonce; set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); r = add_extra_nonce_to_tx_extra(extra, extra_nonce); @@ -4788,19 +4800,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri payment_id_seen = true; message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } - else - { - crypto::hash8 payment_id8; - if (tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8)) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - local_args.pop_back(); - payment_id_seen = true; - } - } - if(!r) { fail_msg_writer() << tr("payment id failed to encode"); @@ -4854,6 +4853,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri info.has_payment_id = true; } de.amount = amount; + de.original = local_args[i]; ++i; } else if (i + 1 < local_args.size()) @@ -4866,6 +4866,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max()); return true; } + de.original = local_args[i]; i += 2; } else @@ -4884,6 +4885,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } de.addr = info.address; de.is_subaddress = info.is_subaddress; + de.is_integrated = info.has_payment_id; num_subaddresses += info.is_subaddress; if (info.has_payment_id || !payment_id_uri.empty()) @@ -4902,6 +4904,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id)) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } @@ -4923,7 +4926,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } // prompt is there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses) + if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -5398,23 +5401,13 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); if(r) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); + std::string extra_nonce; set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); r = add_extra_nonce_to_tx_extra(extra, extra_nonce); payment_id_seen = true; } - else - { - crypto::hash8 payment_id8; - r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8); - if(r) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - payment_id_seen = true; - } - } if(!r && local_args.size() == 3) { @@ -5454,7 +5447,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st } // prompt is there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) + if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -5641,12 +5634,9 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) std::string extra_nonce; if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id)) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); } - else if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8)) - { - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - } else { fail_msg_writer() << tr("failed to parse Payment ID"); @@ -5702,7 +5692,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) } // prompt if there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) + if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -5885,14 +5875,29 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, { if (!payment_id_string.empty()) payment_id_string += ", "; - payment_id_string = std::string("encrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id8); - has_encrypted_payment_id = true; + + // if none of the addresses are integrated addresses, it's a dummy one + bool is_dummy = true; + for (const auto &e: cd.dests) + if (e.is_integrated) + is_dummy = false; + + if (is_dummy) + { + payment_id_string += std::string("dummy encrypted payment ID"); + } + else + { + payment_id_string += std::string("encrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id8); + has_encrypted_payment_id = true; + } } else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { if (!payment_id_string.empty()) payment_id_string += ", "; - payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id); + payment_id_string += std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id); + payment_id_string += " (OBSOLETE)"; } } } @@ -7446,12 +7451,13 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v { if (tools::wallet2::parse_long_payment_id(args[3], payment_id)) { + LONG_PAYMENT_ID_SUPPORT_CHECK(); description_start += 2; } else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id)) { - memcpy(payment_id.data, info.payment_id.data, 8); - description_start += 2; + fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only"); + return true; } else { @@ -7489,7 +7495,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v auto& row = address_book[i]; success_msg_writer() << tr("Index: ") << i; success_msg_writer() << tr("Address: ") << get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address); - success_msg_writer() << tr("Payment ID: ") << row.m_payment_id; + success_msg_writer() << tr("Payment ID: ") << row.m_payment_id << " (OBSOLETE)"; success_msg_writer() << tr("Description: ") << row.m_description << "\n"; } } @@ -8121,6 +8127,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_create_address_file); command_line::add_arg(desc_params, arg_subaddress_lookahead); command_line::add_arg(desc_params, arg_use_english_language_names); + command_line::add_arg(desc_params, arg_long_payment_id_support); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 39b715b73..3ad16cde9 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -357,5 +357,7 @@ namespace cryptonote bool m_auto_refresh_refreshing; std::atomic<bool> m_in_manual_refresh; uint32_t m_current_subaddress_account; + + bool m_long_payment_id_support; }; } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1b4370c36..ccbbe05be 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1386,9 +1386,11 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const if (amount) { vector<cryptonote::tx_destination_entry> dsts; cryptonote::tx_destination_entry de; + de.original = dst_addr; de.addr = info.address; de.amount = *amount; de.is_subaddress = info.is_subaddress; + de.is_integrated = info.has_payment_id; dsts.push_back(de); transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, adjusted_priority, diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1956febb9..0cbb2d286 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1281,6 +1281,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & { case rct::RCTTypeSimple: case rct::RCTTypeBulletproof: + case rct::RCTTypeBulletproof2: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); @@ -1861,7 +1862,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { std::shared_ptr<tools::Notify> tx_notify = m_tx_notify; if (tx_notify) - tx_notify->notify(epee::string_tools::pod_to_hex(txid).c_str()); + tx_notify->notify("%s", epee::string_tools::pod_to_hex(txid).c_str(), NULL); } } //---------------------------------------------------------------------------------------------------- @@ -5563,15 +5564,16 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size()); signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); - rct::RangeProofType range_proof_type = rct::RangeProofBorromean; + rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 }; if (sd.use_bulletproofs) { - range_proof_type = rct::RangeProofPaddedBulletproof; + rct_config.range_proof_type = rct::RangeProofPaddedBulletproof; + rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; } crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; rct::multisig_out msout; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, range_proof_type, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, @@ -5984,15 +5986,16 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto cryptonote::transaction tx; rct::multisig_out msout = ptx.multisig_sigs.front().msout; auto sources = sd.sources; - rct::RangeProofType range_proof_type = rct::RangeProofBorromean; + rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 }; if (sd.use_bulletproofs) { - range_proof_type = rct::RangeProofBulletproof; + rct_config.range_proof_type = rct::RangeProofBulletproof; for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs) if (proof.V.size() > 1) - range_proof_type = rct::RangeProofPaddedBulletproof; + rct_config.range_proof_type = rct::RangeProofPaddedBulletproof; + rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; } - bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false); + bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx), @@ -7143,6 +7146,9 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> break; } } + bool use_histogram = amount != 0 || !has_rct_distribution; + if (!use_histogram) + num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE]; // make sure the real outputs we asked for are really included, along // with the correct key and mask: this guards against an active attack @@ -7350,7 +7356,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent std::vector<crypto::secret_key> additional_tx_keys; rct::multisig_out msout; LOG_PRINT_L2("constructing tx"); - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); @@ -7399,7 +7405,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -7555,7 +7561,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry rct::multisig_out msout; LOG_PRINT_L2("constructing tx"); auto sources_copy = sources; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, range_proof_type, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); @@ -7600,7 +7606,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry LOG_PRINT_L2("Creating supplementary multisig transaction"); cryptonote::transaction ms_tx; auto sources_copy_copy = sources_copy; - bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, range_proof_type, &msout, false); + bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, rct_config, &msout, false); LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); @@ -8277,15 +8283,16 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp TX() : weight(0), needed_fee(0) {} - void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { + void add(const cryptonote::tx_destination_entry &de, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { if (merge_destinations) { std::vector<cryptonote::tx_destination_entry>::iterator i; - i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); + i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &de.addr, sizeof(de.addr)); }); if (i == dsts.end()) { - dsts.push_back(tx_destination_entry(0,addr,is_subaddress)); + dsts.push_back(de); i = dsts.end() - 1; + i->amount = 0; } i->amount += amount; } @@ -8294,8 +8301,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, std::string("original_output_index too large: ") + std::to_string(original_output_index) + " > " + std::to_string(dsts.size())); if (original_output_index == dsts.size()) - dsts.push_back(tx_destination_entry(0,addr,is_subaddress)); - THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address"); + { + dsts.push_back(de); + dsts.back().amount = 0; + } + THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &de.addr, sizeof(de.addr)), error::wallet_internal_error, "Mismatched destination address"); dsts[original_output_index].amount += amount; } } @@ -8307,7 +8317,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); const bool use_rct = use_fork_rules(4, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); - const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean; + const rct::RCTConfig rct_config { + bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean, + bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0 + }; const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -8567,7 +8580,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // we can fully pay that destination LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, dsts[0].is_subaddress, dsts[0].amount, original_output_index, m_merge_destinations); + tx.add(dsts[0], dsts[0].amount, original_output_index, m_merge_destinations); available_amount -= dsts[0].amount; dsts[0].amount = 0; pop_index(dsts, 0); @@ -8578,7 +8591,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, dsts[0].is_subaddress, available_amount, original_output_index, m_merge_destinations); + tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations); dsts[0].amount -= available_amount; available_amount = 0; } @@ -8624,7 +8637,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp tx.selected_transfers.size() << " inputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, range_proof_type); + test_tx, test_ptx, rct_config); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8667,7 +8680,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp while (needed_fee > test_ptx.fee) { if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, range_proof_type); + test_tx, test_ptx, rct_config); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8740,7 +8753,7 @@ skip_tx: extra, /* const std::vector<uint8_t>& extra, */ test_tx, /* OUT cryptonote::transaction& tx, */ test_ptx, /* OUT cryptonote::transaction& tx, */ - range_proof_type); + rct_config); } else { transfer_selected(tx.dsts, tx.selected_transfers, @@ -8880,7 +8893,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE); const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); - const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean; + const rct::RCTConfig rct_config { + bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean, + bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0, + }; const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_quantization_mask = get_fee_quantization_mask(); @@ -8956,7 +8972,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton tx.selected_transfers.size() << " outputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, range_proof_type); + test_tx, test_ptx, rct_config); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8993,7 +9009,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton } if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, range_proof_type); + test_tx, test_ptx, rct_config); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -9032,7 +9048,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton pending_tx test_ptx; if (use_rct) { transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, - test_tx, test_ptx, range_proof_type); + test_tx, test_ptx, rct_config); } else { transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -9621,7 +9637,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de crypto::secret_key scalar1; hwdev.derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); + hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); @@ -10126,7 +10142,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr crypto::secret_key shared_secret; crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret)); + rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2); amount = rct::h2d(ecdh_info.amount); } total += amount; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 680196f01..2114e0eff 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -738,7 +738,7 @@ namespace tools uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type); + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config); void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector<pending_tx>& ptx_vector); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 1b63d65b6..e452d21e8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -644,9 +644,11 @@ namespace tools return false; } + de.original = it->address; de.addr = info.address; de.is_subaddress = info.is_subaddress; de.amount = it->amount; + de.is_integrated = info.has_payment_id; dsts.push_back(de); if (info.has_payment_id) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 20afd4203..89d77cf32 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ add_subdirectory(performance_tests) add_subdirectory(core_proxy) add_subdirectory(unit_tests) add_subdirectory(difficulty) +add_subdirectory(block_weight) add_subdirectory(hash) add_subdirectory(net_load_tests) if (BUILD_GUI_DEPS) @@ -115,6 +116,7 @@ add_test( set(enabled_tests core_tests difficulty + block_weight hash performance_tests core_proxy diff --git a/tests/block_weight/CMakeLists.txt b/tests/block_weight/CMakeLists.txt new file mode 100644 index 000000000..b0d716ea0 --- /dev/null +++ b/tests/block_weight/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(block_weight_sources + block_weight.cpp) + +set(block_weight_headers) + +add_executable(block_weight + ${block_weight_sources} + ${block_weight_headers}) +target_link_libraries(block_weight + PRIVATE + cryptonote_core + blockchain_db + ${EXTRA_LIBRARIES}) + +add_test( + NAME block_weight + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/compare.py ${CMAKE_CURRENT_SOURCE_DIR}/block_weight.py ${CMAKE_CURRENT_BINARY_DIR}/block_weight) diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp new file mode 100644 index 000000000..9b7343ca3 --- /dev/null +++ b/tests/block_weight/block_weight.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define IN_UNIT_TESTS + +#include <stdio.h> +#include <math.h> +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +enum test_t +{ + test_max = 0, + test_lcg = 1, + test_min = 2, +}; + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash() const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; } + virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; } + +private: + std::vector<block_t> blocks; + std::vector<uint8_t> hf; +}; + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)LONG_TERM_BLOCK_WEIGHT_WINDOW), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0); \ + if (!r) \ + { \ + fprintf(stderr, "Failed to init blockchain\n"); \ + exit(1); \ + } + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, LONG_TERM_BLOCK_WEIGHT_WINDOW) + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +static void test(test_t t, uint64_t blocks) +{ + PREFIX(10); + + for (uint64_t h = 0; h < LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + cryptonote::block b; + b.major_version = 1; + b.minor_version = 1; + bc->get_db().add_block(std::move(b), 300000, 300000, bc->get_db().height(), bc->get_db().height(), {}); + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit 1\n"); + exit(1); + } + } + + for (uint64_t h = 0; h < blocks; ++h) + { + uint64_t w; + uint64_t effective_block_weight_median = bc->get_current_cumulative_block_weight_median(); + switch (t) + { + case test_lcg: + { + uint32_t r = lcg(); + int64_t wi = 90 + r % 500000 + 250000 + sin(h / 200.) * 350000; + w = wi < 90 ? 90 : wi; + break; + } + case test_max: + w = bc->get_current_cumulative_block_weight_limit(); + break; + case test_min: + w = 90; + break; + default: + exit(1); + } + uint64_t ltw = bc->get_next_long_term_block_weight(w); + cryptonote::block b; + b.major_version = 10; + b.minor_version = 10; + bc->get_db().add_block(std::move(b), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit 2\n"); + exit(1); + } + if(0)if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit 2\n"); + exit(1); + } +uint64_t last = bc->get_db().get_block_long_term_weight(bc->get_db().height() - 1); +if (last != ltw) std::cout << "Inconsistency at " << h << std::endl; + std::cout << "H " << h << ", BW " << w << ", EMBW " << effective_block_weight_median << ", LTBW " << ltw << std::endl; + } +} + +int main() +{ + mlog_configure("", false); + mlog_set_categories(""); + test(test_max, 2 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_lcg, 9 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_min, 1 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + return 0; +} diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py new file mode 100755 index 000000000..06aaabb02 --- /dev/null +++ b/tests/block_weight/block_weight.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# Simulate a maximal block attack on the Monero network +# This uses the scheme proposed by ArticMine +# Written by Sarang Nother +# Copyright (c) 2019 The Monero Project +import sys +import math + +MEDIAN_WINDOW_SMALL = 100 # number of recent blocks for median computation +MEDIAN_WINDOW_BIG = 5000 +MULTIPLIER_SMALL = 1.4 # multipliers for determining weights +MULTIPLIER_BIG = 50.0 +MEDIAN_THRESHOLD = 300*1000 # initial value for median (scaled kB -> B) +lcg_seed = 0 +embw = MEDIAN_THRESHOLD +ltembw = MEDIAN_THRESHOLD + +weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent +lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + +# Compute the median of a list +def get_median(vec): + #temp = vec + temp = sorted(vec) + if len(temp) % 2 == 1: + return temp[len(temp)/2] + else: + return int((temp[len(temp)/2]+temp[len(temp)/2-1])/2) + +def LCG(): + global lcg_seed + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff + return lcg_seed + +def run(t, blocks): + global embw + global ltembw + + weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent + lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + + for block in range(blocks): + # determine the long-term effective weight + ltmedian = get_median(lt_weights[-MEDIAN_WINDOW_BIG:]) + ltembw = max(MEDIAN_THRESHOLD,ltmedian) + + # determine the effective weight + stmedian = get_median(weights[-MEDIAN_WINDOW_SMALL:]) + embw = min(max(MEDIAN_THRESHOLD,stmedian),int(MULTIPLIER_BIG*ltembw)) + + # drop the lowest values + weights = weights[1:] + lt_weights = lt_weights[1:] + + # add a block of max weight + if t == 0: + max_weight = 2 * embw + elif t == 1: + r = LCG() + max_weight = int(90 + r % 500000 + 250000 + math.sin(block / 200.) * 350000) + if max_weight < 90: max_weight = 90 + elif t == 2: + max_weight = 90 + else: + sys.exit(1) + weights.append(max_weight) + lt_weights.append(min(max_weight,int(ltembw + int(ltembw * 2 / 5)))) + + #print "H %u, r %u, BW %u, EMBW %u, LTBW %u, LTEMBW %u, ltmedian %u" % (block, r, max_weight, embw, lt_weights[-1], ltembw, ltmedian) + print "H %u, BW %u, EMBW %u, LTBW %u" % (block, max_weight, embw, lt_weights[-1]) + +run(0, 2 * MEDIAN_WINDOW_BIG) +run(1, 9 * MEDIAN_WINDOW_BIG) +run(2, 1 * MEDIAN_WINDOW_BIG) diff --git a/tests/block_weight/compare.py b/tests/block_weight/compare.py new file mode 100755 index 000000000..c6be05206 --- /dev/null +++ b/tests/block_weight/compare.py @@ -0,0 +1,13 @@ +#!/usr/bin/python + +import sys +import subprocess + +print 'running: ', sys.argv[1] +S0 = subprocess.check_output(sys.argv[1], stderr=subprocess.STDOUT) +print 'running: ', sys.argv[2] +S1 = subprocess.check_output(sys.argv[2], stderr=subprocess.STDOUT) +print 'comparing' +if S0 != S1: + sys.exit(1) +sys.exit(0) diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index db875d2a2..3e2db2e29 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -42,7 +42,7 @@ using namespace cryptonote; // Tests bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& events, - size_t mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type, + size_t mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, const std::function<bool(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations, size_t tx_idx)> &pre_tx, const std::function<bool(transaction &tx, size_t tx_idx)> &post_tx) const { @@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0}; rct_txes.resize(rct_txes.size() + 1); - bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, range_proof_type[n]); + bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, rct_config[n]); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); if (post_tx && !post_tx(rct_txes.back(), n)) @@ -157,7 +157,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve crypto::secret_key amount_key; crypto::derivation_to_scalar(derivation, o, amount_key); rct::key rct_tx_mask; - if (rct_txes.back().rct_signatures.type == rct::RCTTypeSimple || rct_txes.back().rct_signatures.type == rct::RCTTypeBulletproof) + const uint8_t type = rct_txes.back().rct_signatures.type; + if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2) rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); else rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); @@ -173,8 +174,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs, - 8, 8, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long - crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 8), + 10, 10, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 10), false, "Failed to generate block"); if (!valid) DO_CALLBACK(events, "mark_invalid_block"); @@ -210,16 +211,16 @@ bool gen_bp_tx_valid_1::generate(std::vector<test_event_entry>& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const size_t bp_sizes[] = {1, (size_t)-1}; - const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; - return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); + const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); } bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); + const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, NULL); } bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const @@ -227,8 +228,8 @@ bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1}; - const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; - return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); + const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); } bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const @@ -236,8 +237,8 @@ bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {4, (size_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); }); + const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } }; + return generate_with(events, mixin, 1, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); }); } bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const @@ -245,24 +246,24 @@ bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1}; const size_t bp_sizes[] = {16, (size_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); }); + const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } }; + return generate_with(events, mixin, 1, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); }); } bool gen_bp_tx_invalid_4_2_1::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); + const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, NULL); } bool gen_bp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); + const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, NULL); } bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const @@ -270,25 +271,25 @@ bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) c const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof}; - return generate_with(events, mixin, 2, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); }); + const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 }, {rct::RangeProofPaddedBulletproof, 0 } }; + return generate_with(events, mixin, 2, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); }); } bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = {rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof}; - return generate_with(events, mixin, 3, amounts_paid, false, range_proof_type, NULL, NULL); + const rct::RCTConfig rct_config[] = {{rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}}; + return generate_with(events, mixin, 3, amounts_paid, false, rct_config, NULL, NULL); } bool gen_bp_txs_valid_2_and_3_and_2_and_4::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 10; const uint64_t amounts_paid[] = {11111115000, 11111115000, (uint64_t)-1, 11111115000, 11111115000, 11111115001, (uint64_t)-1, 11111115000, 11111115002, (uint64_t)-1, 11111115000, 11111115000, 11111115000, 11111115003, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof}; + const rct::RCTConfig rct_config[] = {{rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}}; const size_t bp_sizes[] = {2, (size_t)-1, 4, (size_t)-1, 2, (size_t)-1, 4, (size_t)-1}; - return generate_with(events, mixin, 4, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_3_and_2_and_4"); }); + return generate_with(events, mixin, 4, amounts_paid, true, rct_config, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_3_and_2_and_4"); }); } bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const @@ -296,9 +297,9 @@ bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry> DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs"); const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ - CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.pop_back(); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); @@ -311,9 +312,9 @@ bool gen_bp_tx_invalid_empty_proofs::generate(std::vector<test_event_entry>& eve DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_empty_proofs"); const size_t mixin = 10; const uint64_t amounts_paid[] = {50000, 50000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ - CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2); tx.rct_signatures.p.bulletproofs.clear(); return true; }); @@ -324,9 +325,9 @@ bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs"); const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ - CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back()); return true; @@ -338,9 +339,9 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& eve DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount"); const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ - CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen()); return true; @@ -352,10 +353,8 @@ bool gen_bp_tx_invalid_borromean_type::generate(std::vector<test_event_entry>& e DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type"); const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; - return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ - CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); - tx.rct_signatures.type = rct::RCTTypeSimple; + const rct::RCTConfig rct_config[] = { { rct::RangeProofBorromean, 0 } }; + return generate_with(events, mixin, 1, amounts_paid, false, rct_config, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ return true; }); } diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index e29b34690..b0289f355 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -82,7 +82,7 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base } bool generate_with(std::vector<test_event_entry>& events, size_t mixin, - size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type, + size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, const std::function<bool(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations, size_t)> &pre_tx, const std::function<bool(cryptonote::transaction &tx, size_t)> &post_tx) const; @@ -95,7 +95,7 @@ private: template<> struct get_test_options<gen_bp_tx_validation_base> { - const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 73), std::make_pair(0, 0)}; + const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(10, 73), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { hard_forks }; diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index fe5feb942..d6a15b6b4 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -365,7 +365,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry #endif std::vector<crypto::secret_key> additional_tx_secret_keys; auto sources_copy = sources; - r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, rct::RangeProofBorromean, msoutp); + r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, { rct::RangeProofBorromean, 0 }, msoutp); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); #ifndef NO_MULTISIG @@ -455,7 +455,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry crypto::secret_key scalar1; crypto::derivation_to_scalar(derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); + rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2); rct::key C = tx.rct_signatures.outPk[n].mask; rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount"); diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 342c3f1ee..bb7dd013b 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -133,7 +133,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); crypto::secret_key amount_key; crypto::derivation_to_scalar(derivation, o, amount_key); - if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeBulletproof) + const uint8_t type = rct_txes[n].rct_signatures.type; + if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2) rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); else rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index ecb23818c..cd2bcb839 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -81,7 +81,7 @@ template<> struct get_test_options<gen_v2_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 433cf94e9..105cf2c13 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -43,7 +43,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow slow-1 slow-2 slow-4 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 7767d0d3b..acfd99e96 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -44,6 +44,13 @@ using namespace std; using namespace crypto; typedef crypto::hash chash; +struct V4_Data +{ + const void* data; + size_t length; + uint64_t height; +}; + PUSH_WARNINGS DISABLE_VS_WARNINGS(4297) extern "C" { @@ -54,13 +61,17 @@ extern "C" { tree_hash((const char (*)[crypto::HASH_SIZE]) data, length >> 5, hash); } static void cn_slow_hash_0(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_1(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_2(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/, 0/*height*/); + } + static void cn_slow_hash_4(const void *data, size_t, char *hash) { + const V4_Data* p = reinterpret_cast<const V4_Data*>(data); + return cn_slow_hash(p->data, p->length, hash, 4/*variant*/, 0/*prehashed*/, p->height); } } POP_WARNINGS @@ -72,7 +83,7 @@ struct hash_func { } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, - {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}}; + {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}, {"slow-4", cn_slow_hash_4}}; int test_variant2_int_sqrt(); int test_variant2_int_sqrt_ref(); @@ -140,7 +151,15 @@ int main(int argc, char *argv[]) { input.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); input.clear(input.rdstate()); get(input, data); - f(data.data(), data.size(), (char *) &actual); + if (f == cn_slow_hash_4) { + V4_Data d; + d.data = data.data(); + d.length = data.size(); + get(input, d.height); + f(&d, 0, (char *) &actual); + } else { + f(data.data(), data.size(), (char *) &actual); + } if (expected != actual) { size_t i; cerr << "Hash mismatch on test " << test << endl << "Input: "; diff --git a/tests/hash/tests-slow-4.txt b/tests/hash/tests-slow-4.txt new file mode 100644 index 000000000..06ab52cff --- /dev/null +++ b/tests/hash/tests-slow-4.txt @@ -0,0 +1,10 @@ +f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 1806260 +5bb833deca2bdd7252a9ccd7b4ce0b6a4854515794b56c207262f7a5b9bdb566 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 1806261 +1ee6728da60fbd8d7d55b2b1ade487a3cf52a2c3ac6f520db12c27d8921f6cab 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 1806262 +6969fe2ddfb758438d48049f302fc2108a4fcc93e37669170e6db4b0b9b4c4cb 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c 1806263 +7f3048b4e90d0cbe7a57c0394f37338a01fae3adfdc0e5126d863a895eb04e02 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 1806264 +1d290443a4b542af04a82f6b2494a6ee7f20f2754c58e0849032483a56e8e2ef 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 1806265 +c43cc6567436a86afbd6aa9eaa7c276e9806830334b614b2bee23cc76634f6fd 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 1806266 +87be2479c0c4e8edfdfaa5603e93f4265b3f8224c1c5946feb424819d18990a4 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e 1806267 +dd9d6a6d8e47465cceac0877ef889b93e7eba979557e3935d7f86dce11b070f3 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c 1806268 +75c6f2ae49a20521de97285b431e717125847fb8935ed84a61e7f8d36a2c3d8e 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e 1806269 diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index ee382d9ad..755d8f941 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -40,7 +40,7 @@ #include "multi_tx_test_base.h" -template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean> +template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, int bp_version = 2> class test_check_tx_signature : private multi_tx_test_base<a_ring_size> { static_assert(0 < a_ring_size, "ring_size must be greater than 0"); @@ -71,7 +71,8 @@ public: std::vector<crypto::secret_key> additional_tx_keys; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, range_proof_type)) + rct::RCTConfig rct_config{range_proof_type, bp_version}; + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, rct_config)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); @@ -135,7 +136,7 @@ public: m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0)); for (size_t n = 0; n < a_num_txes; ++n) { - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofPaddedBulletproof)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, true, {rct::RangeProofPaddedBulletproof, 2})) return false; } @@ -146,7 +147,7 @@ public: for (size_t n = 1; n < extra_outs; ++n) destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes.back(), 0, tx_key, additional_tx_keys, true, rct::RangeProofMultiOutputBulletproof)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes.back(), 0, tx_key, additional_tx_keys, true, {rct::RangeProofMultiOutputBulletproof, 2})) return false; } diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 378065ddd..71d42073d 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -36,7 +36,7 @@ #include "multi_tx_test_base.h" -template<size_t a_in_count, size_t a_out_count, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean> +template<size_t a_in_count, size_t a_out_count, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, int bp_version = 2> class test_construct_tx : private multi_tx_test_base<a_in_count> { static_assert(0 < a_in_count, "in_count must be greater than 0"); @@ -73,7 +73,8 @@ public: std::vector<crypto::secret_key> additional_tx_keys; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, range_proof_type); + rct::RCTConfig rct_config{range_proof_type, bp_version}; + return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, rct_config); } private: diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 87a1573c2..e3c053ff8 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -132,17 +132,17 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof, 2); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false); @@ -155,14 +155,14 @@ int main(int argc, char** argv) TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof, 2); + TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof, 2); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index d6bcbde46..3bb2914f4 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -54,6 +54,7 @@ set(unit_tests_sources hashchain.cpp http.cpp keccak.cpp + long_term_block_weight.cpp main.cpp memwipe.cpp mlocker.cpp @@ -61,6 +62,7 @@ set(unit_tests_sources mul_div.cpp multiexp.cpp multisig.cpp + notify.cpp parse_amount.cpp random.cpp serialization.cpp diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 7e7ce9bf7..9cf8c5fbe 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -277,10 +277,10 @@ TYPED_TEST(BlockchainDBTest, AddBlock) // TODO: need at least one more block to make this reasonable, as the // BlockchainDB implementation should not check for parent if // no blocks have been added yet (because genesis has no parent). - //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); + //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); block b; ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0]))); @@ -293,7 +293,7 @@ TYPED_TEST(BlockchainDBTest, AddBlock) ASSERT_TRUE(compare_blocks(this->m_blocks[0], b)); // assert that we can't add the same block twice - ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); + ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); for (auto& h : this->m_blocks[0].tx_hashes) { @@ -317,14 +317,14 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) this->get_filenames(); this->init_hard_fork(); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1)); ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), this->m_db->get_block_hash_from_height(0)); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index ac6eaca0b..4f07415b0 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -131,7 +131,8 @@ TEST(bulletproofs, multi_splitting) } rct::ctkeyV outSk; - rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofPaddedBulletproof, hw::get_device("default")); + rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 0 }; + rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct_config, hw::get_device("default")); ASSERT_TRUE(rct::verRctSimple(s)); for (size_t i = 0; i < n_outputs; ++i) { diff --git a/tests/unit_tests/device.cpp b/tests/unit_tests/device.cpp index 50ccec9fa..064a7028e 100644 --- a/tests/unit_tests/device.cpp +++ b/tests/unit_tests/device.cpp @@ -114,7 +114,7 @@ TEST(device, ops) ASSERT_EQ(ki0, ki1); } -TEST(device, ecdh) +TEST(device, ecdh32) { hw::core::device_default dev; rct::ecdhTuple tuple, tuple2; @@ -123,9 +123,10 @@ TEST(device, ecdh) tuple.amount = rct::skGen(); tuple.senderPk = rct::pkGen(); tuple2 = tuple; - dev.ecdhEncode(tuple, key); - dev.ecdhDecode(tuple, key); + dev.ecdhEncode(tuple, key, false); + dev.ecdhDecode(tuple, key, false); ASSERT_EQ(tuple2.mask, tuple.mask); ASSERT_EQ(tuple2.amount, tuple.amount); ASSERT_EQ(tuple2.senderPk, tuple.senderPk); } + diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 47177db1c..5dd93dfaa 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -34,6 +34,7 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" +#include "blockchain_db/testdb.h" using namespace cryptonote; @@ -41,7 +42,7 @@ using namespace cryptonote; #define SECONDS_PER_YEAR 31557600 -class TestDB: public BlockchainDB { +class TestDB: public cryptonote::BaseTestDB { public: TestDB() {}; virtual void open(const std::string& filename, const int db_flags = 0) { } @@ -131,6 +132,7 @@ public: virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -186,20 +188,20 @@ TEST(major, Only) ASSERT_FALSE(hf.add(mkblock(0, 2), 0)); ASSERT_FALSE(hf.add(mkblock(2, 2), 0)); ASSERT_TRUE(hf.add(mkblock(1, 2), 0)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 1, only version 1 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 1)); ASSERT_FALSE(hf.add(mkblock(2, 2), 1)); ASSERT_TRUE(hf.add(mkblock(1, 2), 1)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 2, only version 2 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 2)); ASSERT_FALSE(hf.add(mkblock(1, 2), 2)); ASSERT_FALSE(hf.add(mkblock(3, 2), 2)); ASSERT_TRUE(hf.add(mkblock(2, 2), 2)); - db.add_block(mkblock(2, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(2, 1), 0, 0, 0, 0, 0, crypto::hash()); } TEST(empty_hardforks, Success) @@ -213,7 +215,7 @@ TEST(empty_hardforks, Success) ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); for (uint64_t h = 0; h <= 10; ++h) { - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } ASSERT_EQ(hf.get(0), 1); @@ -247,14 +249,14 @@ TEST(check_for_height, Success) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 10; ++h) { ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -271,19 +273,19 @@ TEST(get, next_version) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_EQ(2, hf.get_next_version()); - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 9; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 10; h <= 15; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -324,7 +326,7 @@ TEST(steps_asap, Success) hf.init(); for (uint64_t h = 0; h < 10; ++h) { - db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -351,7 +353,7 @@ TEST(steps_1, Success) hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { - db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -376,7 +378,7 @@ TEST(reorganize, Same) // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -407,7 +409,7 @@ TEST(reorganize, Changed) static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); } @@ -427,7 +429,7 @@ TEST(reorganize, Changed) ASSERT_EQ(db.height(), 3); hf.reorganize_from_block_height(2); for (uint64_t h = 3; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ (ret, h < 15); } @@ -451,7 +453,7 @@ TEST(voting, threshold) for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 @@ -485,7 +487,7 @@ TEST(voting, different_thresholds) static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ(ret, true); } @@ -539,7 +541,7 @@ TEST(voting, info) ASSERT_EQ(expected_thresholds[h], threshold); ASSERT_EQ(4, voting); - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -602,7 +604,7 @@ TEST(reorganize, changed) #define ADD(v, h, a) \ do { \ cryptonote::block b = mkblock(hf, h, v); \ - db.add_block(b, 0, 0, 0, 0, crypto::hash()); \ + db.add_block(b, 0, 0, 0, 0, 0, crypto::hash()); \ ASSERT_##a(hf.add(b, h)); \ } while(0) #define ADD_TRUE(v, h) ADD(v, h, TRUE) diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp index 234cb2c33..53d9f84c7 100644 --- a/tests/unit_tests/json_serialization.cpp +++ b/tests/unit_tests/json_serialization.cpp @@ -75,7 +75,7 @@ namespace std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[from.m_account_address.m_spend_public_key] = {0,0}; - if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean)) + if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, { bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean, bulletproof ? 2 : 0 })) throw std::runtime_error{"transaction construction error"}; return tx; diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp new file mode 100644 index 000000000..9bfd95e0e --- /dev/null +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -0,0 +1,384 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define IN_UNIT_TESTS + +#include "gtest/gtest.h" +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash() const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + +private: + std::vector<block_t> blocks; +}; + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0); \ + ASSERT_TRUE(r) + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) + +TEST(long_term_block_weight, empty_short) +{ + PREFIX(9); + + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * 2); +} + +TEST(long_term_block_weight, identical_before_fork) +{ + PREFIX(9); + + for (uint64_t h = 1; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, identical_after_fork_before_long_term_window) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, ceiling_at_30000000) +{ + PREFIX(10); + + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 2 - 1; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), 15000000); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), 30000000); +} + +TEST(long_term_block_weight, multi_pop) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + 20; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + + for (uint64_t h = 0; h < 4; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); +} + +TEST(long_term_block_weight, multiple_updates) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= 3 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } +} + +TEST(long_term_block_weight, pop_invariant_max) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 12; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, pop_invariant_random) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 12; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + lcg_seed = bc->get_db().height(); + uint32_t r = lcg(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, long_growth_spike_and_drop) +{ + PREFIX(10); + + uint64_t long_term_effective_median_block_weight; + + // constant init + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_EQ(long_term_effective_median_block_weight, 300000); + + // slow 10% yearly for a year (scaled down by 100000 / TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) -> 8% change + for (uint64_t h = 0; h < 365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + //size_t w = bc->get_current_cumulative_block_weight_median() * rate; + float t = h / float(365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000); + size_t w = 300000 + t * 30000; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // spike over three weeks - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // drop - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_median() * .25; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); +} diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp new file mode 100644 index 000000000..ceeba8649 --- /dev/null +++ b/tests/unit_tests/notify.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifdef __GLIBC__ +#include <sys/stat.h> +#endif + +#include "gtest/gtest.h" + +#include <boost/filesystem.hpp> + +#include "misc_language.h" +#include "string_tools.h" +#include "file_io_utils.h" +#include "common/notify.h" + +TEST(notify, works) +{ +#ifdef __GLIBC__ + mode_t prevmode = umask(077); +#endif + const char *tmp = getenv("TEMP"); + if (!tmp) + tmp = "/tmp"; + static const char *filename = "monero-notify-unit-test-XXXXXX"; + const size_t len = strlen(tmp) + 1 + strlen(filename); + std::unique_ptr<char[]> name_template_(new char[len + 1]); + char *name_template = name_template_.get(); + ASSERT_TRUE(name_template != NULL); + snprintf(name_template, len + 1, "%s/%s", tmp, filename); + int fd = mkstemp(name_template); +#ifdef __GLIBC__ + umask(prevmode); +#endif + ASSERT_TRUE(fd >= 0); + close(fd); + + const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier" +#ifdef _WIN32 + + ".exe" +#endif + + " " + name_template + " %s"; + + tools::Notify notify(spec.c_str()); + notify.notify("%s", "1111111111111111111111111111111111111111111111111111111111111111", NULL); + + bool ok = false; + for (int i = 0; i < 10; ++i) + { + epee::misc_utils::sleep_no_w(100); + + std::string s; + if (epee::file_io_utils::load_file_to_string(name_template, s)) + { + if (s == "1111111111111111111111111111111111111111111111111111111111111111") + { + ok = true; + break; + } + std::cout << "got: [" << s << "]" << std::endl; + } + } + boost::filesystem::remove(name_template); + ASSERT_TRUE(ok); +} diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 3877ef785..cd514798b 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -171,8 +171,10 @@ TEST(ringct, range_proofs) skpkGen(Sk, Pk); destinations.push_back(Pk); + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); //verify rct data ASSERT_TRUE(verRct(s)); @@ -189,7 +191,7 @@ TEST(ringct, range_proofs) //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); //verify rct data ASSERT_FALSE(verRct(s)); @@ -235,8 +237,10 @@ TEST(ringct, range_proofs_with_fee) skpkGen(Sk, Pk); destinations.push_back(Pk); + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); //verify rct data ASSERT_TRUE(verRct(s)); @@ -253,7 +257,7 @@ TEST(ringct, range_proofs_with_fee) //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); //verify rct data ASSERT_FALSE(verRct(s)); @@ -311,7 +315,8 @@ TEST(ringct, simple) //compute sig with mixin 2 xmr_amount txnfee = 1; - rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, amount_keys, NULL, NULL, txnfee, 2, hw::get_device("default")); + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, amount_keys, NULL, NULL, txnfee, 2, rct_config, hw::get_device("default")); //verify ring ct signature ASSERT_TRUE(verRctSimple(s)); @@ -345,7 +350,8 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount } } - return genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + return genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); } static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], uint64_t fee) @@ -371,7 +377,8 @@ static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input destinations.push_back(Pk); } - return genRctSimple(rct::zero(), sc, pc, destinations, inamounts, outamounts, amount_keys, NULL, NULL, fee, 3, hw::get_device("default")); + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + return genRctSimple(rct::zero(), sc, pc, destinations, inamounts, outamounts, amount_keys, NULL, NULL, fee, 3, rct_config, hw::get_device("default")); } static bool range_proof_test(bool expected_valid, @@ -824,27 +831,6 @@ TEST(ringct, HPow2) static const xmr_amount test_amounts[]={0, 1, 2, 3, 4, 5, 10000, 10000000000000000000ull, 10203040506070809000ull, 123456789123456789}; -TEST(ringct, ecdh_roundtrip) -{ - key k; - ecdhTuple t0, t1; - - for (auto amount: test_amounts) { - skGen(k); - - t0.mask = skGen(); - t0.amount = d2h(amount); - - t1 = t0; - ecdhEncode(t1, k); - ecdhDecode(t1, k); - ASSERT_TRUE(t0.mask == t1.mask); - ASSERT_TRUE(equalKeys(t0.mask, t1.mask)); - ASSERT_TRUE(t0.amount == t1.amount); - ASSERT_TRUE(equalKeys(t0.amount, t1.amount)); - } -} - TEST(ringct, d2h) { key k, P1; diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 2f7b5aac7..aed919bca 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -591,7 +591,8 @@ TEST(Serialization, serializes_ringct_types) rct::skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, hw::get_device("default")); + const rct::RCTConfig rct_config{ rct::RangeProofPaddedBulletproof, 0 }; + s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); mg0 = s0.p.MGs[0]; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); |