Merge pull request #4219

9137ad2c blockchain: add a testnet v9 a day after v8 (moneromooo-monero)
ac4f71c2 wallet2: bump testnet rollback to account for coming reorg (moneromooo-monero)
8f418a6d bulletproofs: #include <openssl/bn.h> (moneromooo-monero)
2bf63650 bulletproofs: speed up the latest changes a bit (moneromooo-monero)
044dff5a bulletproofs: scale points by 8 to ensure subgroup validity (moneromooo-monero)
c83012c4 bulletproofs: match aggregated verification to sarang's latest prototype (moneromooo-monero)
ce0c7432 performance_tests: add padded bulletproof construction (moneromooo-monero)
1224e53b core_tests: add a test for 4-aggregated BP verification (moneromooo-monero)
0e6ed559 fuzz_tests: add a bulletproof fuzz test (moneromooo-monero)
463434d1 more comprehensive test for ge_p3 comparison to identity/point at infinity (moneromooo-monero)
d0a0565f unit_tests: add a few more multiexp unit tests (moneromooo-monero)
6526d87f core_tests: add a test for a tx with empty bulletproof (moneromooo-monero)
a129bbd9 multiexp: fix maxscalar off by one (moneromooo-monero)
7ed496cc ringct: error out when hashToPoint* returns the point at infinity (moneromooo-monero)
d1591853 cryptonote_basic: check output type before using it (moneromooo-monero)
61632dc1 ringct: prevent a potential very large allocation (moneromooo-monero)
a4317e61 crypto: some paranoid checks in generate_signature/check_signature (moneromooo-monero)
7434df1c crypto: never return zero in random32_unbiased (moneromooo-monero)
0825e974 multiexp: fix wrong Bos-Coster result for 1 non trivial input (moneromooo-monero)
a1359ad4 Check inputs to addKeys are in range (moneromooo-monero)
fe0fa3b9 bulletproofs: reject x, y, z, or w[i] being zero (moneromooo-monero)
5ffb2ff9 v8: per byte fee, pad bulletproofs, fixed 11 ring size (moneromooo-monero)
869b3bf8 bulletproofs: a few fixes from the Kudelski review (moneromooo-monero)
c4291762 bulletproofs: reject points not in the main subgroup (moneromooo-monero)
15697177 bulletproofs: speed up a few multiplies using existing Hi cache (moneromooo-monero)
0b05a0fa Add Pippenger cache and limit Straus cache size (moneromooo-monero)
51eb3bdc add pippenger unit tests (moneromooo-monero)
b17b8db3 performance_tests: add stats and loop count multiplier options (moneromooo-monero)
7314d919 perf_timer: split timer class into a base one and a logging one (moneromooo-monero)
d126a02b performance_tests: add aggregated bulletproof tx verification (moneromooo-monero)
263431c4 Pippenger multiexp (moneromooo-monero)
1ed0ed4d multiexp: cut down on memory allocations (moneromooo-monero)
1b867e7f precalc the ge_p3 representation of H (moneromooo-monero)
ef56529f performance_tests: document the tested bulletproof layouts (moneromooo-monero)
30111780 unit_tests: a couple more bulletproof unit tests for gamma (moneromooo-monero)
c444b1b2 require canonical multi output bulletproof layout (moneromooo-monero)
7e67c52f Add a define for the max number of bulletproof multi-outputs (moneromooo-monero)
2a8fcb42 Bulletproof aggregated verification and tests (moneromooo-monero)
126196b0 multiexp: some speedups (moneromooo-monero)
71d67bda aligned: aligned memory alloc/realloc/free (moneromooo-monero)
cb9ecab1 performance_tests: add signature generation/verification (moneromooo-monero)
bacf0a1e bulletproofs: add aggregated verification (moneromooo-monero)
e895c3de make straus cached mode thread safe, and add tests for it (moneromooo-monero)
7f48bf05 multiexp: bos coster now works for just one point (moneromooo-monero)
9ce9f8ca bulletproofs: add multi output bulletproofs to rct (moneromooo-monero)
f34e2e20 performance_tests: add tx checking tests with more than 2 outputs (moneromooo-monero)
0793184b performance_tests: add a --verbose flag, and default to terse (moneromooo-monero)
939bc223 add Straus multiexp (moneromooo-monero)
9ff6e6a0 ringct: add bos coster multiexp (moneromooo-monero)
e9164bb3 bulletproofs: misc optimizations (moneromooo-monero)
112f32f0 performance_tests: add crypto ops (moneromooo-monero)
f5d7b993 performance_tests: add bulletproofs (moneromooo-monero)
8f4ce989 performance_tests: add RingCT MLSAG gen/ver tests (moneromooo-monero)
1aa10c43 performance_tests: add (Borromean) range proofs (moneromooo-monero)
aacfd6e3 bulletproofs: multi-output bulletproofs (moneromooo-monero)
cb1cc757 performance_tests: don't override log level to 0 (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2018-09-11 15:45:56 +02:00
commit e6d36c1701
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
95 changed files with 5688 additions and 1255 deletions

View File

@ -10,12 +10,12 @@ fi
type="$1" type="$1"
if test -z "$type" if test -z "$type"
then then
echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin" echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof"
exit 1 exit 1
fi fi
case "$type" in case "$type" in
block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin) ;; block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof) ;;
*) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin"; exit 1 ;; *) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof"; exit 1 ;;
esac esac
if test -d "fuzz-out/$type" if test -d "fuzz-out/$type"

View File

@ -224,7 +224,7 @@ struct Dbt_safe : public Dbt
namespace cryptonote namespace cryptonote
{ {
void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) void BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash)
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -255,7 +255,7 @@ void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const
if (res) if (res)
throw0(DB_ERROR("Failed to add block blob to db transaction.")); throw0(DB_ERROR("Failed to add block blob to db transaction."));
Dbt_copy<size_t> sz(block_size); Dbt_copy<size_t> sz(block_weight);
if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0)) if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0))
throw0(DB_ERROR("Failed to add block size to db transaction.")); throw0(DB_ERROR("Failed to add block size to db transaction."));
@ -1353,7 +1353,7 @@ uint64_t BlockchainBDB::get_top_block_timestamp() const
return get_block_timestamp(m_height - 1); return get_block_timestamp(m_height - 1);
} }
size_t BlockchainBDB::get_block_size(const uint64_t& height) const size_t BlockchainBDB::get_block_weight(const uint64_t& height) const
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -1861,7 +1861,7 @@ void BlockchainBDB::block_txn_abort()
// TODO // TODO
} }
uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs) uint64_t BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs)
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -1874,7 +1874,7 @@ uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, co
uint64_t num_outputs = m_num_outputs; uint64_t num_outputs = m_num_outputs;
try try
{ {
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs);
m_write_txn = NULL; m_write_txn = NULL;
TIME_MEASURE_START(time1); TIME_MEASURE_START(time1);

View File

@ -266,7 +266,7 @@ public:
virtual uint64_t get_top_block_timestamp() const; virtual uint64_t get_top_block_timestamp() const;
virtual size_t get_block_size(const uint64_t& height) const; virtual size_t get_block_weight(const uint64_t& height) const;
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
@ -318,7 +318,7 @@ public:
virtual bool has_key_image(const crypto::key_image& img) const; virtual bool has_key_image(const crypto::key_image& img) const;
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -353,7 +353,7 @@ public:
private: private:
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const crypto::hash& block_hash , const crypto::hash& block_hash

View File

@ -196,7 +196,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
} }
uint64_t BlockchainDB::add_block( const block& blk uint64_t BlockchainDB::add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -241,7 +241,7 @@ uint64_t BlockchainDB::add_block( const block& blk
// call out to subclass implementation to add the block & metadata // call out to subclass implementation to add the block & metadata
time1 = epee::misc_utils::get_tick_count(); time1 = epee::misc_utils::get_tick_count();
add_block(blk, block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); add_block(blk, block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash);
TIME_MEASURE_FINISH(time1); TIME_MEASURE_FINISH(time1);
time_add_block1 += time1; time_add_block1 += time1;

View File

@ -137,7 +137,7 @@ struct txpool_tx_meta_t
{ {
crypto::hash max_used_block_id; crypto::hash max_used_block_id;
crypto::hash last_failed_id; crypto::hash last_failed_id;
uint64_t blob_size; uint64_t weight;
uint64_t fee; uint64_t fee;
uint64_t max_used_block_height; uint64_t max_used_block_height;
uint64_t last_failed_height; uint64_t last_failed_height;
@ -358,13 +358,13 @@ private:
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
* *
* @param blk the block to be added * @param blk the block to be added
* @param block_size the size of the block (transactions and all) * @param block_weight the weight of the block (transactions and all)
* @param cumulative_difficulty the accumulated difficulty after this block * @param cumulative_difficulty the accumulated difficulty after this block
* @param coins_generated the number of coins generated total after this block * @param coins_generated the number of coins generated total after this block
* @param blk_hash the hash of the block * @param blk_hash the hash of the block
*/ */
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs
@ -376,7 +376,7 @@ private:
* *
* The subclass implementing this will remove the block data from the top * 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 * block in the chain. The data to be removed is that which was added in
* BlockchainDB::add_block(const block& blk, const size_t& block_size, 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, 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 * If any of this cannot be done, the subclass should throw the corresponding
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
@ -789,7 +789,7 @@ public:
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
* *
* @param blk the block to be added * @param blk the block to be added
* @param block_size the size of the block (transactions and all) * @param block_weight the size of the block (transactions and all)
* @param cumulative_difficulty the accumulated difficulty after this block * @param cumulative_difficulty the accumulated difficulty after this block
* @param coins_generated the number of coins generated total after this block * @param coins_generated the number of coins generated total after this block
* @param txs the transactions in the block * @param txs the transactions in the block
@ -797,7 +797,7 @@ public:
* @return the height of the chain post-addition * @return the height of the chain post-addition
*/ */
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -930,18 +930,18 @@ public:
virtual uint64_t get_top_block_timestamp() const = 0; virtual uint64_t get_top_block_timestamp() const = 0;
/** /**
* @brief fetch a block's size * @brief fetch a block's weight
* *
* The subclass should return the size of the block with the * The subclass should return the weight of the block with the
* given height. * given height.
* *
* If the block does not exist, the subclass should throw BLOCK_DNE * If the block does not exist, the subclass should throw BLOCK_DNE
* *
* @param height the height requested * @param height the height requested
* *
* @return the size * @return the weight
*/ */
virtual size_t get_block_size(const uint64_t& height) const = 0; virtual size_t get_block_weight(const uint64_t& height) const = 0;
/** /**
* @brief fetch a block's cumulative difficulty * @brief fetch a block's cumulative difficulty

View File

@ -256,7 +256,7 @@ typedef struct mdb_block_info_old
uint64_t bi_height; uint64_t bi_height;
uint64_t bi_timestamp; uint64_t bi_timestamp;
uint64_t bi_coins; uint64_t bi_coins;
uint64_t bi_size; // a size_t really but we need 32-bit compat uint64_t bi_weight; // a size_t really but we need 32-bit compat
difficulty_type bi_diff; difficulty_type bi_diff;
crypto::hash bi_hash; crypto::hash bi_hash;
} mdb_block_info_old; } mdb_block_info_old;
@ -266,7 +266,7 @@ typedef struct mdb_block_info
uint64_t bi_height; uint64_t bi_height;
uint64_t bi_timestamp; uint64_t bi_timestamp;
uint64_t bi_coins; uint64_t bi_coins;
uint64_t bi_size; // a size_t really but we need 32-bit compat uint64_t bi_weight; // a size_t really but we need 32-bit compat
difficulty_type bi_diff; difficulty_type bi_diff;
crypto::hash bi_hash; crypto::hash bi_hash;
uint64_t bi_cum_rct; uint64_t bi_cum_rct;
@ -652,8 +652,11 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
block_rtxn_start(&rtxn, &rcurs); block_rtxn_start(&rtxn, &rcurs);
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
{ {
uint32_t block_size = get_block_size(block_num); // we have access to block weight, which will be greater or equal to block size,
total_block_size += block_size; // so use this as a proxy. If it's too much off, we might have to check actual size,
// which involves reading more data, so is not really wanted
size_t block_weight = get_block_weight(block_num);
total_block_size += block_weight;
// Track number of blocks being totalled here instead of assuming, in case // Track number of blocks being totalled here instead of assuming, in case
// some blocks were to be skipped for being outliers. // some blocks were to be skipped for being outliers.
++num_blocks_used; ++num_blocks_used;
@ -674,7 +677,7 @@ estim:
return threshold_size; return threshold_size;
} }
void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
uint64_t num_rct_outs, const crypto::hash& blk_hash) uint64_t num_rct_outs, const crypto::hash& blk_hash)
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -720,7 +723,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
bi.bi_height = m_height; bi.bi_height = m_height;
bi.bi_timestamp = blk.timestamp; bi.bi_timestamp = blk.timestamp;
bi.bi_coins = coins_generated; bi.bi_coins = coins_generated;
bi.bi_size = block_size; bi.bi_weight = block_weight;
bi.bi_diff = cumulative_difficulty; bi.bi_diff = cumulative_difficulty;
bi.bi_hash = blk_hash; bi.bi_hash = blk_hash;
bi.bi_cum_rct = num_rct_outs; bi.bi_cum_rct = num_rct_outs;
@ -743,7 +746,9 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str()));
m_cum_size += block_size; // we use weight as a proxy for size, since we don't have size but weight is >= size
// and often actually equal
m_cum_size += block_weight;
m_cum_count++; m_cum_count++;
} }
@ -2011,7 +2016,7 @@ uint64_t BlockchainLMDB::get_top_block_timestamp() const
return get_block_timestamp(m_height - 1); return get_block_timestamp(m_height - 1);
} }
size_t BlockchainLMDB::get_block_size(const uint64_t& height) const size_t BlockchainLMDB::get_block_weight(const uint64_t& height) const
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open(); check_open();
@ -2029,7 +2034,7 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const
throw0(DB_ERROR("Error attempting to retrieve a block size from the db")); throw0(DB_ERROR("Error attempting to retrieve a block size from the db"));
mdb_block_info *bi = (mdb_block_info *)result.mv_data; mdb_block_info *bi = (mdb_block_info *)result.mv_data;
size_t ret = bi->bi_size; size_t ret = bi->bi_weight;
TXN_POSTFIX_RDONLY(); TXN_POSTFIX_RDONLY();
return ret; return ret;
} }
@ -3090,7 +3095,7 @@ void BlockchainLMDB::block_txn_abort()
} }
} }
uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
const std::vector<transaction>& txs) const std::vector<transaction>& txs)
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -3109,7 +3114,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
try try
{ {
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs);
} }
catch (const DB_ERROR_TXN_START& e) catch (const DB_ERROR_TXN_START& e)
{ {
@ -3698,9 +3703,9 @@ void BlockchainLMDB::migrate_0_1()
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str()));
if (v.mv_size == sizeof(uint32_t)) if (v.mv_size == sizeof(uint32_t))
bi.bi_size = *(uint32_t *)v.mv_data; bi.bi_weight = *(uint32_t *)v.mv_data;
else else
bi.bi_size = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0 bi.bi_weight = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0
result = mdb_cursor_get(c_timestamps, &k, &v, MDB_NEXT); result = mdb_cursor_get(c_timestamps, &k, &v, MDB_NEXT);
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_timestamps: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to get a record from block_timestamps: ", result).c_str()));
@ -4259,7 +4264,7 @@ void BlockchainLMDB::migrate_2_3()
bi.bi_height = bi_old->bi_height; bi.bi_height = bi_old->bi_height;
bi.bi_timestamp = bi_old->bi_timestamp; bi.bi_timestamp = bi_old->bi_timestamp;
bi.bi_coins = bi_old->bi_coins; bi.bi_coins = bi_old->bi_coins;
bi.bi_size = bi_old->bi_size; bi.bi_weight = bi_old->bi_weight;
bi.bi_diff = bi_old->bi_diff; bi.bi_diff = bi_old->bi_diff;
bi.bi_hash = bi_old->bi_hash; bi.bi_hash = bi_old->bi_hash;
if (bi_old->bi_height >= distribution.size()) if (bi_old->bi_height >= distribution.size())

View File

@ -205,7 +205,7 @@ public:
virtual uint64_t get_top_block_timestamp() const; virtual uint64_t get_top_block_timestamp() const;
virtual size_t get_block_size(const uint64_t& height) const; virtual size_t get_block_weight(const uint64_t& height) const;
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
@ -273,7 +273,7 @@ public:
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -317,7 +317,7 @@ private:
uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const; uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs

View File

@ -474,17 +474,17 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
txs.push_back(tx); txs.push_back(tx);
} }
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
uint64_t coins_generated; uint64_t coins_generated;
block_size = bp.block_size; block_weight = bp.block_weight;
cumulative_difficulty = bp.cumulative_difficulty; cumulative_difficulty = bp.cumulative_difficulty;
coins_generated = bp.coins_generated; coins_generated = bp.coins_generated;
try try
{ {
core.get_blockchain_storage().get_db().add_block(b, block_size, cumulative_difficulty, coins_generated, txs); core.get_blockchain_storage().get_db().add_block(b, block_weight, cumulative_difficulty, coins_generated, txs);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

View File

@ -236,11 +236,11 @@ void BootstrapFile::write_block(block& block)
bool include_extra_block_data = true; bool include_extra_block_data = true;
if (include_extra_block_data) if (include_extra_block_data)
{ {
size_t block_size = m_blockchain_storage->get_db().get_block_size(block_height); size_t block_weight = m_blockchain_storage->get_db().get_block_weight(block_height);
difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height); difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height);
uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height); uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height);
bp.block_size = block_size; bp.block_weight = block_weight;
bp.cumulative_difficulty = cumulative_difficulty; bp.cumulative_difficulty = cumulative_difficulty;
bp.coins_generated = coins_generated; bp.coins_generated = coins_generated;
} }

View File

@ -70,14 +70,14 @@ namespace cryptonote
{ {
cryptonote::block block; cryptonote::block block;
std::vector<transaction> txs; std::vector<transaction> txs;
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
uint64_t coins_generated; uint64_t coins_generated;
BEGIN_SERIALIZE() BEGIN_SERIALIZE()
FIELD(block) FIELD(block)
FIELD(txs) FIELD(txs)
VARINT_FIELD(block_size) VARINT_FIELD(block_weight)
VARINT_FIELD(cumulative_difficulty) VARINT_FIELD(cumulative_difficulty)
VARINT_FIELD(coins_generated) VARINT_FIELD(coins_generated)
END_SERIALIZE() END_SERIALIZE()

View File

@ -38,7 +38,8 @@ set(common_sources
password.cpp password.cpp
perf_timer.cpp perf_timer.cpp
threadpool.cpp threadpool.cpp
updates.cpp) updates.cpp
aligned.c)
if (STACK_TRACE) if (STACK_TRACE)
list(APPEND common_sources stack_trace.cpp) list(APPEND common_sources stack_trace.cpp)
@ -67,7 +68,8 @@ set(common_private_headers
perf_timer.h perf_timer.h
stack_trace.h stack_trace.h
threadpool.h threadpool.h
updates.h) updates.h
aligned.h)
monero_private_headers(common monero_private_headers(common
${common_private_headers}) ${common_private_headers})

139
src/common/aligned.c Normal file
View File

@ -0,0 +1,139 @@
// Copyright (c) 2017-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.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include "aligned.h"
static inline int is_power_of_2(size_t n) { return n && (n & (n-1)) == 0; }
#define MAGIC 0xaa0817161500ff81
#define MAGIC_FREED 0xaa0817161500ff82
static void local_abort(const char *msg)
{
fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG
_exit(1);
#else
abort();
#endif
}
typedef struct
{
uint64_t magic;
void *raw;
size_t bytes;
size_t align;
} control;
void *aligned_malloc(size_t bytes, size_t align)
{
void *raw, *ptr;
control *ctrl;
if (!is_power_of_2(align))
return NULL;
if (bytes > (size_t)-1 - align)
return NULL;
if (bytes + align > (size_t)-1 - sizeof(control))
return NULL;
raw = malloc(bytes + sizeof(control) + align);
if (!raw)
return NULL;
ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align-1));
ctrl = ((control*)ptr) - 1;
ctrl->magic = MAGIC;
ctrl->raw = raw;
ctrl->bytes = bytes;
ctrl->align = align;
return ptr;
}
void *aligned_realloc(void *ptr, size_t bytes, size_t align)
{
void *raw, *ptr2;
control *ctrl, *ctrl2;
if (!ptr)
return aligned_malloc(bytes, align);
if (!bytes)
{
aligned_free(ptr);
return NULL;
}
if (!is_power_of_2(align))
return NULL;
ctrl = ((control*)ptr) - 1;
if (ctrl->magic == MAGIC_FREED)
local_abort("Double free detected");
if (ctrl->magic != MAGIC)
local_abort("Freeing unallocated memory");
if (ctrl->align != align)
return NULL;
if (ctrl->bytes >= bytes)
return ptr;
if (ctrl->bytes > (size_t)-1 - ctrl->align)
return NULL;
if (ctrl->bytes + ctrl->align > (size_t)-1 - sizeof(control))
return NULL;
raw = malloc(bytes + sizeof(control) + ctrl->align);
if (!raw)
return NULL;
ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align-1));
memcpy(ptr2, ptr, ctrl->bytes);
ctrl2 = ((control*)ptr2) - 1;
ctrl2->magic = MAGIC;
ctrl2->raw = raw;
ctrl2->bytes = bytes;
ctrl2->align = ctrl->align;
ctrl->magic = MAGIC_FREED;
free(ctrl->raw);
return ptr2;
}
void aligned_free(void *ptr)
{
if (!ptr)
return;
control *ctrl = ((control*)ptr) - 1;
if (ctrl->magic == MAGIC_FREED)
local_abort("Double free detected");
if (ctrl->magic != MAGIC)
local_abort("Freeing unallocated memory");
ctrl->magic = MAGIC_FREED;
free(ctrl->raw);
}

41
src/common/aligned.h Normal file
View File

@ -0,0 +1,41 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void *aligned_malloc(size_t bytes, size_t align);
void *aligned_realloc(void *ptr, size_t bytes, size_t align);
void aligned_free(void *ptr);
#ifdef __cplusplus
}
#endif

View File

@ -33,7 +33,7 @@
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "perf" #define MONERO_DEFAULT_LOG_CATEGORY "perf"
namespace namespace tools
{ {
uint64_t get_tick_count() uint64_t get_tick_count()
{ {
@ -83,7 +83,7 @@ namespace tools
el::Level performance_timer_log_level = el::Level::Debug; el::Level performance_timer_log_level = el::Level::Debug;
static __thread std::vector<PerformanceTimer*> *performance_timers = NULL; static __thread std::vector<LoggingPerformanceTimer*> *performance_timers = NULL;
void set_performance_timer_log_level(el::Level level) void set_performance_timer_log_level(el::Level level)
{ {
@ -96,17 +96,24 @@ void set_performance_timer_log_level(el::Level level)
performance_timer_log_level = level; performance_timer_log_level = level;
} }
PerformanceTimer::PerformanceTimer(const std::string &s, uint64_t unit, el::Level l): name(s), unit(unit), level(l), started(false), paused(false) PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused)
{ {
if (paused)
ticks = 0;
else
ticks = get_tick_count(); ticks = get_tick_count();
}
LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l): PerformanceTimer(), name(s), unit(unit), level(l)
{
if (!performance_timers) if (!performance_timers)
{ {
MLOG(level, "PERF ----------"); MLOG(level, "PERF ----------");
performance_timers = new std::vector<PerformanceTimer*>(); performance_timers = new std::vector<LoggingPerformanceTimer*>();
} }
else else
{ {
PerformanceTimer *pt = performance_timers->back(); LoggingPerformanceTimer *pt = performance_timers->back();
if (!pt->started && !pt->paused) if (!pt->started && !pt->paused)
{ {
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size;
@ -119,9 +126,14 @@ PerformanceTimer::PerformanceTimer(const std::string &s, uint64_t unit, el::Leve
PerformanceTimer::~PerformanceTimer() PerformanceTimer::~PerformanceTimer()
{ {
performance_timers->pop_back();
if (!paused) if (!paused)
ticks = get_tick_count() - ticks; ticks = get_tick_count() - ticks;
}
LoggingPerformanceTimer::~LoggingPerformanceTimer()
{
pause();
performance_timers->pop_back();
char s[12]; char s[12];
snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit)));
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size;

View File

@ -43,30 +43,46 @@ class PerformanceTimer;
extern el::Level performance_timer_log_level; extern el::Level performance_timer_log_level;
uint64_t get_tick_count();
uint64_t get_ticks_per_ns();
uint64_t ticks_to_ns(uint64_t ticks);
class PerformanceTimer class PerformanceTimer
{ {
public: public:
PerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug); PerformanceTimer(bool paused = false);
~PerformanceTimer(); ~PerformanceTimer();
void pause(); void pause();
void resume(); void resume();
private: uint64_t value() const { return ticks; }
std::string name; void set(uint64_t v){ticks=v;}
uint64_t unit;
el::Level level; protected:
uint64_t ticks; uint64_t ticks;
bool started; bool started;
bool paused; bool paused;
}; };
class LoggingPerformanceTimer: public PerformanceTimer
{
public:
LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug);
~LoggingPerformanceTimer();
private:
std::string name;
uint64_t unit;
el::Level level;
};
void set_performance_timer_log_level(el::Level level); void set_performance_timer_log_level(el::Level level);
#define PERF_TIMER_UNIT(name, unit) tools::PerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level) #define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level)
#define PERF_TIMER_UNIT_L(name, unit, l) tools::PerformanceTimer pt_##name(#name, unit, l) #define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, unit, l)
#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000) #define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000)
#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l) #define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l)
#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::PerformanceTimer> pt_##name(new tools::PerformanceTimer(#name, unit, el::Level::Info)) #define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, unit, el::Level::Info))
#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000) #define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000)
#define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0) #define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0)
#define PERF_TIMER_PAUSE(name) pt_##name->pause() #define PERF_TIMER_PAUSE(name) pt_##name->pause()

View File

@ -871,3 +871,9 @@ const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -
const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */
const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */
const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} }; const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} };
const ge_p3 ge_p3_H = {
{7329926, -15101362, 31411471, 7614783, 27996851, -3197071, -11157635, -6878293, 466949, -7986503},
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
};

View File

@ -3707,9 +3707,8 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b,
s[31] = s11 >> 17; s[31] = s11 >> 17;
} }
/* Assumes that a != INT64_MIN */
static int64_t signum(int64_t a) { static int64_t signum(int64_t a) {
return (a >> 63) - ((-a) >> 63); return a > 0 ? 1 : a < 0 ? -1 : 0;
} }
int sc_check(const unsigned char *s) { int sc_check(const unsigned char *s) {
@ -3730,3 +3729,16 @@ int sc_isnonzero(const unsigned char *s) {
s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] |
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
} }
int ge_p3_is_point_at_infinity(const ge_p3 *p) {
// X = 0 and Y == Z
int n;
for (n = 0; n < 10; ++n)
{
if (p->X[n] | p->T[n])
return 0;
if (p->Y[n] != p->Z[n])
return 0;
}
return 1;
}

View File

@ -140,6 +140,7 @@ extern const fe fe_fffb2;
extern const fe fe_fffb3; extern const fe fe_fffb3;
extern const fe fe_fffb4; extern const fe fe_fffb4;
extern const ge_p3 ge_p3_identity; extern const ge_p3 ge_p3_identity;
extern const ge_p3 ge_p3_H;
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
void sc_0(unsigned char *); void sc_0(unsigned char *);
void sc_reduce32(unsigned char *); void sc_reduce32(unsigned char *);
@ -158,3 +159,5 @@ void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void fe_add(fe h, const fe f, const fe g); void fe_add(fe h, const fe f, const fe g);
void fe_tobytes(unsigned char *, const fe); void fe_tobytes(unsigned char *, const fe);
void fe_invert(fe out, const fe z); void fe_invert(fe out, const fe z);
int ge_p3_is_point_at_infinity(const ge_p3 *p);

View File

@ -116,7 +116,7 @@ namespace crypto {
do do
{ {
generate_random_bytes_thread_safe(32, bytes); generate_random_bytes_thread_safe(32, bytes);
} while (!less32(bytes, limit)); // should be good about 15/16 of the time } while (!sc_isnonzero(bytes) && !less32(bytes, limit)); // should be good about 15/16 of the time
sc_reduce32(bytes); sc_reduce32(bytes);
} }
/* generate a random 32-byte (256-bit) integer and copy it to res */ /* generate a random 32-byte (256-bit) integer and copy it to res */
@ -274,11 +274,18 @@ namespace crypto {
#endif #endif
buf.h = prefix_hash; buf.h = prefix_hash;
buf.key = pub; buf.key = pub;
try_again:
random_scalar(k); random_scalar(k);
if (((const uint32_t*)(&k))[7] == 0) // we don't want tiny numbers here
goto try_again;
ge_scalarmult_base(&tmp3, &k); ge_scalarmult_base(&tmp3, &k);
ge_p3_tobytes(&buf.comm, &tmp3); ge_p3_tobytes(&buf.comm, &tmp3);
hash_to_scalar(&buf, sizeof(s_comm), sig.c); hash_to_scalar(&buf, sizeof(s_comm), sig.c);
if (!sc_isnonzero((const unsigned char*)sig.c.data))
goto try_again;
sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k); sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k);
if (!sc_isnonzero((const unsigned char*)sig.r.data))
goto try_again;
} }
bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) {
@ -292,11 +299,14 @@ namespace crypto {
if (ge_frombytes_vartime(&tmp3, &pub) != 0) { if (ge_frombytes_vartime(&tmp3, &pub) != 0) {
return false; return false;
} }
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) { if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0 || !sc_isnonzero(&sig.c)) {
return false; return false;
} }
ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r);
ge_tobytes(&buf.comm, &tmp2); ge_tobytes(&buf.comm, &tmp2);
static const ec_point infinity = {{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
if (memcmp(&buf.comm, &infinity, 32) == 0)
return false;
hash_to_scalar(&buf, sizeof(s_comm), c); hash_to_scalar(&buf, sizeof(s_comm), c);
sc_sub(&c, &c, &sig.c); sc_sub(&c, &c, &sig.c);
return sc_isnonzero(&c) == 0; return sc_isnonzero(&c) == 0;

View File

@ -67,7 +67,7 @@ namespace cryptonote {
/* Cryptonote helper functions */ /* Cryptonote helper functions */
/************************************************************************/ /************************************************************************/
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
size_t get_min_block_size(uint8_t version) size_t get_min_block_weight(uint8_t version)
{ {
if (version < 2) if (version < 2)
return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
@ -86,7 +86,7 @@ namespace cryptonote {
return CRYPTONOTE_MAX_TX_SIZE; return CRYPTONOTE_MAX_TX_SIZE;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) { bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) {
static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60"); static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60");
const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
const int target_minutes = target / 60; const int target_minutes = target / 60;
@ -98,37 +98,37 @@ namespace cryptonote {
base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes;
} }
uint64_t full_reward_zone = get_min_block_size(version); uint64_t full_reward_zone = get_min_block_weight(version);
//make it soft //make it soft
if (median_size < full_reward_zone) { if (median_weight < full_reward_zone) {
median_size = full_reward_zone; median_weight = full_reward_zone;
} }
if (current_block_size <= median_size) { if (current_block_weight <= median_weight) {
reward = base_reward; reward = base_reward;
return true; return true;
} }
if(current_block_size > 2 * median_size) { if(current_block_weight > 2 * median_weight) {
MERROR("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size); MERROR("Block cumulative weight is too big: " << current_block_weight << ", expected less than " << 2 * median_weight);
return false; return false;
} }
assert(median_size < std::numeric_limits<uint32_t>::max()); assert(median_weight < std::numeric_limits<uint32_t>::max());
assert(current_block_size < std::numeric_limits<uint32_t>::max()); assert(current_block_weight < std::numeric_limits<uint32_t>::max());
uint64_t product_hi; uint64_t product_hi;
// BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being // BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being
// treated as 32-bit by default. // treated as 32-bit by default.
uint64_t multiplicand = 2 * median_size - current_block_size; uint64_t multiplicand = 2 * median_weight - current_block_weight;
multiplicand *= current_block_size; multiplicand *= current_block_weight;
uint64_t product_lo = mul128(base_reward, multiplicand, &product_hi); uint64_t product_lo = mul128(base_reward, multiplicand, &product_hi);
uint64_t reward_hi; uint64_t reward_hi;
uint64_t reward_lo; uint64_t reward_lo;
div128_32(product_hi, product_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo); div128_32(product_hi, product_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo); div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
assert(0 == reward_hi); assert(0 == reward_hi);
assert(reward_lo < base_reward); assert(reward_lo < base_reward);

View File

@ -86,10 +86,10 @@ namespace cryptonote {
/************************************************************************/ /************************************************************************/
/* Cryptonote helper functions */ /* Cryptonote helper functions */
/************************************************************************/ /************************************************************************/
size_t get_min_block_size(uint8_t version); size_t get_min_block_weight(uint8_t version);
size_t get_max_block_size(); size_t get_max_block_size();
size_t get_max_tx_size(); size_t get_max_tx_size();
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version);
uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_address_checksum(const public_address_outer_blob& bl);
uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl);

View File

@ -295,7 +295,7 @@ namespace boost
a & x.type; a & x.type;
if (x.type == rct::RCTTypeNull) if (x.type == rct::RCTTypeNull)
return; return;
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); 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.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 // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
@ -323,7 +323,7 @@ namespace boost
a & x.type; a & x.type;
if (x.type == rct::RCTTypeNull) if (x.type == rct::RCTTypeNull)
return; return;
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); 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.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 // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
@ -337,7 +337,7 @@ namespace boost
if (x.p.rangeSigs.empty()) if (x.p.rangeSigs.empty())
a & x.p.bulletproofs; a & x.p.bulletproofs;
a & x.p.MGs; a & x.p.MGs;
if (x.type == rct::RCTTypeSimpleBulletproof) if (x.type == rct::RCTTypeBulletproof)
a & x.p.pseudoOuts; a & x.p.pseudoOuts;
} }
} }

View File

@ -135,23 +135,41 @@ namespace cryptonote
return false; return false;
} }
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
{
if (tx.vout[n].target.type() != typeid(txout_to_key))
{
LOG_PRINT_L1("Unsupported output type in tx " << get_transaction_hash(tx));
return false;
}
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key); rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
}
if (!base_only) if (!base_only)
{ {
const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof; const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof) if (bulletproof)
{ {
if (rv.p.bulletproofs.size() != tx.vout.size()) if (rv.p.bulletproofs.size() != 1)
{ {
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx)); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx));
return false; return false;
} }
for (size_t n = 0; n < rv.outPk.size(); ++n) if (rv.p.bulletproofs[0].L.size() < 6)
{ {
rv.p.bulletproofs[n].V.resize(1); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs L size in tx " << get_transaction_hash(tx));
rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; return false;
} }
const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6);
if (max_outputs < tx.vout.size())
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx));
return false;
}
const size_t n_amounts = tx.vout.size();
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT);
} }
} }
} }
@ -318,6 +336,37 @@ namespace cryptonote
return string_tools::get_xtype_from_string(amount, str_amount); return string_tools::get_xtype_from_string(amount, str_amount);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
{
if (tx.version < 2)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_bulletproof(rv.type))
return blob_size;
const size_t n_outputs = tx.vout.size();
if (n_outputs <= 2)
return blob_size;
const uint64_t bp_base = 368;
const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
size_t nlr = 0;
for (const auto &bp: rv.p.bulletproofs)
nlr += bp.L.size() * 2;
const size_t bp_size = 32 * (9 + nlr);
CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback");
const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
}
//---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx)
{
std::ostringstream s;
binary_archive<true> a(s);
::serialization::serialize(a, const_cast<transaction&>(tx));
const cryptonote::blobdata blob = s.str();
return get_transaction_weight(tx, blob.size());
}
//---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee) bool get_tx_fee(const transaction& tx, uint64_t & fee)
{ {
if (tx.version > 1) if (tx.version > 1)

View File

@ -117,6 +117,8 @@ namespace cryptonote
bool check_inputs_types_supported(const transaction& tx); bool check_inputs_types_supported(const transaction& tx);
bool check_outs_valid(const transaction& tx); bool check_outs_valid(const transaction& tx);
bool parse_amount(uint64_t& amount, const std::string& str_amount); bool parse_amount(uint64_t& amount, const std::string& str_amount);
uint64_t get_transaction_weight(const transaction &tx);
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size);
bool check_money_overflow(const transaction& tx); bool check_money_overflow(const transaction& tx);
bool check_outs_overflow(const transaction& tx); bool check_outs_overflow(const transaction& tx);

View File

@ -65,9 +65,11 @@
#define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) #define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10)
#define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9)
#define FEE_PER_BYTE ((uint64_t)300000)
#define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9)
#define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12)
#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) #define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5)
#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT ((uint64_t)3000)
#define ORPHANED_BLOCKS_MAX_COUNT 100 #define ORPHANED_BLOCKS_MAX_COUNT 100
@ -133,13 +135,17 @@
#define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_DYNAMIC_FEE 4
#define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_4 6
#define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_MIN_MIXIN_6 7
#define HF_VERSION_MIN_MIXIN_10 8
#define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_ENFORCE_RCT 6
#define HF_VERSION_PER_BYTE_FEE 8
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8
#define HASH_OF_HASHES_STEP 256 #define HASH_OF_HASHES_STEP 256
#define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes #define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes
#define BULLETPROOF_MAX_OUTPUTS 16
// New constants are intended to go here // New constants are intended to go here
namespace config namespace config

View File

@ -132,6 +132,7 @@ static const struct {
{ 6, 971400, 0, 1501709789 }, { 6, 971400, 0, 1501709789 },
{ 7, 1057027, 0, 1512211236 }, { 7, 1057027, 0, 1512211236 },
{ 8, 1057058, 0, 1515967497 }, { 8, 1057058, 0, 1515967497 },
{ 9, 1057778, 0, 1515967498 },
}; };
static const uint64_t testnet_hard_fork_version_1_till = 624633; static const uint64_t testnet_hard_fork_version_1_till = 624633;
@ -155,7 +156,7 @@ static const struct {
//------------------------------------------------------------------ //------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) : 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_sz_limit(0), m_current_block_cumul_sz_median(0), 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_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_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1), m_difficulty_for_next_block(1),
@ -482,7 +483,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
} }
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -631,7 +632,7 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_txs_check.clear(); m_blocks_txs_check.clear();
m_check_txin_table.clear(); m_check_txin_table.clear();
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
invalidate_block_template_cache(); invalidate_block_template_cache();
@ -650,7 +651,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
block_verification_context bvc = boost::value_initialized<block_verification_context>(); block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc); add_new_block(b, bvc);
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -1113,7 +1114,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
// This function validates the miner transaction reward // This function validates the miner transaction reward
bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version) bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
//validate reward //validate reward
@ -1131,11 +1132,11 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
} }
} }
std::vector<size_t> last_blocks_sizes; std::vector<size_t> last_blocks_weights;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version)) if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version))
{ {
MERROR_VER("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
return false; return false;
} }
if(base_reward + fee < money_in_use) if(base_reward + fee < money_in_use)
@ -1165,8 +1166,8 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
// get the block sizes of the last <count> blocks, and return by reference <sz>. // get the block weights of the last <count> blocks, and return by reference <sz>.
void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
@ -1177,26 +1178,26 @@ void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count)
return; return;
m_db->block_txn_start(true); m_db->block_txn_start(true);
// add size of last <count> blocks to vector <sz> (or less, if blockchain size < count) // add weight of last <count> blocks to vector <weights> (or less, if blockchain size < count)
size_t start_offset = h - std::min<size_t>(h, count); size_t start_offset = h - std::min<size_t>(h, count);
sz.reserve(sz.size() + h - start_offset); weights.reserve(weights.size() + h - start_offset);
for(size_t i = start_offset; i < h; i++) for(size_t i = start_offset; i < h; i++)
{ {
sz.push_back(m_db->get_block_size(i)); weights.push_back(m_db->get_block_weight(i));
} }
m_db->block_txn_stop(); m_db->block_txn_stop();
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_blocksize_limit() const uint64_t Blockchain::get_current_cumulative_block_weight_limit() const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
return m_current_block_cumul_sz_limit; return m_current_block_cumul_weight_limit;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_blocksize_median() const uint64_t Blockchain::get_current_cumulative_block_weight_median() const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
return m_current_block_cumul_sz_median; return m_current_block_cumul_weight_median;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
//TODO: This function only needed minor modification to work with BlockchainDB, //TODO: This function only needed minor modification to work with BlockchainDB,
@ -1213,7 +1214,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_median() const
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size; size_t median_weight;
uint64_t already_generated_coins; uint64_t already_generated_coins;
uint64_t pool_cookie; uint64_t pool_cookie;
@ -1250,20 +1251,20 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
diffic = get_difficulty_for_next_block(); diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
median_size = m_current_block_cumul_sz_limit / 2; median_weight = m_current_block_cumul_weight_limit / 2;
already_generated_coins = m_db->get_block_already_generated_coins(height - 1); already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
CRITICAL_REGION_END(); CRITICAL_REGION_END();
size_t txs_size; size_t txs_weight;
uint64_t fee; uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
{ {
return false; return false;
} }
pool_cookie = m_tx_pool.cookie(); pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0; size_t real_txs_weight = 0;
uint64_t real_fee = 0; uint64_t real_fee = 0;
CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
for(crypto::hash &cur_hash: b.tx_hashes) for(crypto::hash &cur_hash: b.tx_hashes)
@ -1275,11 +1276,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
continue; continue;
} }
tx_memory_pool::tx_details &cur_tx = cur_res->second; tx_memory_pool::tx_details &cur_tx = cur_res->second;
real_txs_size += cur_tx.blob_size; real_txs_weight += cur_tx.weight;
real_fee += cur_tx.fee; real_fee += cur_tx.fee;
if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) if (cur_tx.weight != get_transaction_weight(cur_tx.tx))
{ {
LOG_ERROR("Creating block template: error: invalid transaction size"); LOG_ERROR("Creating block template: error: invalid transaction weight");
} }
if (cur_tx.tx.version == 1) if (cur_tx.tx.version == 1)
{ {
@ -1301,9 +1302,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
} }
} }
} }
if (txs_size != real_txs_size) if (txs_weight != real_txs_weight)
{ {
LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); LOG_ERROR("Creating block template: error: wrongly calculated transaction weight");
} }
if (fee != real_fee) if (fee != real_fee)
{ {
@ -1311,70 +1312,70 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
} }
CRITICAL_REGION_END(); CRITICAL_REGION_END();
MDEBUG("Creating block template: height " << height << MDEBUG("Creating block template: height " << height <<
", median size " << median_size << ", median weight " << median_weight <<
", already generated coins " << already_generated_coins << ", already generated coins " << already_generated_coins <<
", transaction size " << txs_size << ", transaction weight " << txs_weight <<
", fee " << fee); ", fee " << fee);
#endif #endif
/* /*
two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know two-phase miner transaction generation: we don't know exact block weight until we prepare block, but we don't know reward until we know
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight
*/ */
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size //make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
uint8_t hf_version = m_hardfork->get_current_version(); uint8_t hf_version = m_hardfork->get_current_version();
size_t max_outs = hf_version >= 4 ? 1 : 11; size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << MDEBUG("Creating block template: miner tx weight " << get_transaction_weight(b.miner_tx) <<
", cumulative size " << cumulative_size); ", cumulative weight " << cumulative_weight);
#endif #endif
for (size_t try_count = 0; try_count != 10; ++try_count) for (size_t try_count = 0; try_count != 10; ++try_count)
{ {
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); size_t coinbase_weight = get_transaction_weight(b.miner_tx);
if (coinbase_blob_size > cumulative_size - txs_size) if (coinbase_weight > cumulative_weight - txs_weight)
{ {
cumulative_size = txs_size + coinbase_blob_size; cumulative_weight = txs_weight + coinbase_weight;
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << cumulative_size << " is greater than before"); ", cumulative weight " << cumulative_weight << " is greater than before");
#endif #endif
continue; continue;
} }
if (coinbase_blob_size < cumulative_size - txs_size) if (coinbase_weight < cumulative_weight - txs_weight)
{ {
size_t delta = cumulative_size - txs_size - coinbase_blob_size; size_t delta = cumulative_weight - txs_weight - coinbase_weight;
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << txs_size + coinbase_blob_size << ", cumulative weight " << txs_weight + coinbase_weight <<
" is less than before, adding " << delta << " zero bytes"); " is less than before, adding " << delta << " zero bytes");
#endif #endif
b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
//here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx))
{ {
CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); CHECK_AND_ASSERT_MES(cumulative_weight + 1 == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " + 1 is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx));
b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx))
{ {
//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_weight
MDEBUG("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1); MDEBUG("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1);
cumulative_size += delta - 1; cumulative_weight += delta - 1;
continue; continue;
} }
MDEBUG("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count); MDEBUG("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count);
} }
} }
CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); CHECK_AND_ASSERT_MES(cumulative_weight == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx));
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << cumulative_size << " is now good"); ", cumulative weight " << cumulative_weight << " is now good");
#endif #endif
cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie); cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
@ -2540,7 +2541,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
if(m_show_time_stats) if(m_show_time_stats)
{ {
size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx) << " W: " << get_transaction_weight(tx));
} }
if (!res) if (!res)
return false; return false;
@ -2597,7 +2598,8 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from v8, allow bulletproofs // from v8, allow bulletproofs
if (hf_version < 8) { if (hf_version < 8) {
const bool bulletproof = tx.rct_signatures.type == rct::RCTTypeFullBulletproof || tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof; if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{ {
MERROR("Bulletproofs are not allowed before v8"); MERROR("Bulletproofs are not allowed before v8");
@ -2605,6 +2607,20 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return false; return false;
} }
} }
}
// from v9, forbid borromean range proofs
if (hf_version > 8) {
if (tx.version >= 2) {
const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type);
if (borromean)
{
MERROR("Borromean range proofs are not allowed after v8");
tvc.m_invalid_output = true;
return false;
}
}
}
return true; return true;
} }
@ -2631,7 +2647,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.message = rct::hash2rct(tx_prefix_hash); rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways // mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) if (rv.type == rct::RCTTypeFull)
{ {
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size()); rv.mixRing.resize(pubkeys[0].size());
@ -2646,7 +2662,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
} }
} }
} }
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof)
{ {
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size()); rv.mixRing.resize(pubkeys.size());
@ -2665,14 +2681,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
} }
// II // II
if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) if (rv.type == rct::RCTTypeFull)
{ {
rv.p.MGs.resize(1); rv.p.MGs.resize(1);
rv.p.MGs[0].II.resize(tx.vin.size()); rv.p.MGs[0].II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n) 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); 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::RCTTypeSimpleBulletproof) else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof)
{ {
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); 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) for (size_t n = 0; n < tx.vin.size(); ++n)
@ -2714,7 +2730,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{ {
size_t n_unmixable = 0, n_mixable = 0; size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max(); size_t mixin = std::numeric_limits<size_t>::max();
const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2;
for (const auto& txin : tx.vin) for (const auto& txin : tx.vin)
{ {
// non txin_to_key inputs will be rejected below // non txin_to_key inputs will be rejected below
@ -2743,6 +2759,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
} }
} }
if (hf_version >= HF_VERSION_MIN_MIXIN_10 && mixin != 10)
{
MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11");
tvc.m_low_mixin = true;
return false;
}
if (mixin < min_mixin) if (mixin < min_mixin)
{ {
if (n_unmixable == 0) if (n_unmixable == 0)
@ -2938,7 +2961,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false; return false;
} }
case rct::RCTTypeSimple: case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof:
{ {
// check all this, either reconstructed (so should really pass), or not // check all this, either reconstructed (so should really pass), or not
{ {
@ -2988,7 +3011,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
} }
} }
if (!rct::verRctSimple(rv, false)) if (!rct::verRctNonSemanticsSimple(rv))
{ {
MERROR_VER("Failed to check ringct signatures!"); MERROR_VER("Failed to check ringct signatures!");
return false; return false;
@ -2996,7 +3019,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
break; break;
} }
case rct::RCTTypeFull: case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
{ {
// check all this, either reconstructed (so should really pass), or not // check all this, either reconstructed (so should really pass), or not
{ {
@ -3059,6 +3081,22 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Unsupported rct type: " << rv.type); MERROR_VER("Unsupported rct type: " << rv.type);
return false; return false;
} }
// for bulletproofs, check they're only multi-output after v8
if (rct::is_rct_bulletproof(rv.type))
{
if (hf_version < 8)
{
for (const rct::Bulletproof &proof: rv.p.bulletproofs)
{
if (proof.V.size() > 1)
{
MERROR_VER("Multi output bulletproofs are invalid before v8");
return false;
}
}
}
}
} }
return true; return true;
} }
@ -3078,7 +3116,7 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
static uint64_t get_fee_quantization_mask() uint64_t Blockchain::get_fee_quantization_mask()
{ {
static uint64_t mask = 0; static uint64_t mask = 0;
if (mask == 0) if (mask == 0)
@ -3091,16 +3129,27 @@ static uint64_t get_fee_quantization_mask()
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version)
{ {
const uint64_t min_block_size = get_min_block_size(version); const uint64_t min_block_weight = get_min_block_weight(version);
const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; if (median_block_weight < min_block_weight)
median_block_weight = min_block_weight;
uint64_t hi, lo;
if (median_block_size < min_block_size) if (version >= HF_VERSION_PER_BYTE_FEE)
median_block_size = min_block_size; {
lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi);
div128_32(hi, lo, min_block_weight, &hi, &lo);
div128_32(hi, lo, median_block_weight, &hi, &lo);
assert(hi == 0);
lo /= 5;
return lo;
}
uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size); const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi);
uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight);
lo = mul128(unscaled_fee_base, block_reward, &hi);
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000");
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large");
@ -3118,10 +3167,33 @@ uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
uint64_t median = 0;
uint64_t already_generated_coins = 0;
uint64_t base_reward = 0;
if (version >= HF_VERSION_DYNAMIC_FEE)
{
median = m_current_block_cumul_weight_limit / 2;
already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
}
uint64_t needed_fee;
if (version >= HF_VERSION_PER_BYTE_FEE)
{
uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
const uint64_t mask = get_fee_quantization_mask();
needed_fee = (needed_fee + mask - 1) / mask * mask;
}
else
{
uint64_t fee_per_kb; uint64_t fee_per_kb;
if (version < HF_VERSION_DYNAMIC_FEE) if (version < HF_VERSION_DYNAMIC_FEE)
{ {
@ -3129,18 +3201,14 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
} }
else else
{ {
uint64_t median = m_current_block_cumul_sz_limit / 2; fee_per_kb = get_dynamic_base_fee(base_reward, median, version);
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version);
} }
MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee"); MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee");
uint64_t needed_fee = blob_size / 1024; needed_fee = tx_weight / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee += (tx_weight % 1024) ? 1 : 0;
needed_fee *= fee_per_kb; needed_fee *= fee_per_kb;
}
if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow
{ {
@ -3151,7 +3219,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
@ -3161,16 +3229,16 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
const uint64_t min_block_size = get_min_block_size(version); const uint64_t min_block_weight = get_min_block_weight(version);
std::vector<size_t> sz; std::vector<size_t> weights;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
sz.reserve(grace_blocks); weights.reserve(grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i) for (size_t i = 0; i < grace_blocks; ++i)
sz.push_back(min_block_size); weights.push_back(min_block_weight);
uint64_t median = epee::misc_utils::median(sz); uint64_t median = epee::misc_utils::median(weights);
if(median <= min_block_size) if(median <= min_block_weight)
median = min_block_size; median = min_block_weight;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward; uint64_t base_reward;
@ -3180,8 +3248,9 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
base_reward = BLOCK_REWARD_OVERESTIMATE; base_reward = BLOCK_REWARD_OVERESTIMATE;
} }
uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); uint64_t fee = get_dynamic_base_fee(base_reward, median, version);
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); 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; return fee;
} }
@ -3357,11 +3426,11 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
for (const auto &txid: txids) for (const auto &txid: txids)
{ {
cryptonote::transaction tx; cryptonote::transaction tx;
size_t blob_size; size_t tx_weight;
uint64_t fee; uint64_t fee;
bool relayed, do_not_relay, double_spend_seen; bool relayed, do_not_relay, double_spend_seen;
MINFO("Removing txid " << txid << " from the pool"); MINFO("Removing txid " << txid << " from the pool");
if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{ {
MERROR("Failed to remove txid " << txid << " from the pool"); MERROR("Failed to remove txid " << txid << " from the pool");
res = false; res = false;
@ -3521,8 +3590,8 @@ leave:
goto leave; goto leave;
} }
size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
size_t cumulative_block_size = coinbase_blob_size; size_t cumulative_block_weight = coinbase_weight;
std::vector<transaction> txs; std::vector<transaction> txs;
key_images_container keys; key_images_container keys;
@ -3544,7 +3613,7 @@ leave:
for (const crypto::hash& tx_id : bl.tx_hashes) for (const crypto::hash& tx_id : bl.tx_hashes)
{ {
transaction tx; transaction tx;
size_t blob_size = 0; size_t tx_weight = 0;
uint64_t fee = 0; uint64_t fee = 0;
bool relayed = false, do_not_relay = false, double_spend_seen = false; bool relayed = false, do_not_relay = false, double_spend_seen = false;
TIME_MEASURE_START(aa); TIME_MEASURE_START(aa);
@ -3563,7 +3632,7 @@ leave:
TIME_MEASURE_START(bb); TIME_MEASURE_START(bb);
// get transaction with hash <tx_id> from tx_pool // get transaction with hash <tx_id> from tx_pool
if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) if(!m_tx_pool.take_tx(tx_id, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{ {
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
bvc.m_verifivation_failed = true; bvc.m_verifivation_failed = true;
@ -3634,7 +3703,7 @@ leave:
TIME_MEASURE_FINISH(cc); TIME_MEASURE_FINISH(cc);
t_checktx += cc; t_checktx += cc;
fee_summary += fee; fee_summary += fee;
cumulative_block_size += blob_size; cumulative_block_weight += tx_weight;
} }
m_blocks_txs_check.clear(); m_blocks_txs_check.clear();
@ -3642,7 +3711,7 @@ leave:
TIME_MEASURE_START(vmt); TIME_MEASURE_START(vmt);
uint64_t base_reward = 0; uint64_t base_reward = 0;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version())) if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version()))
{ {
MERROR_VER("Block with id: " << id << " has incorrect miner transaction"); MERROR_VER("Block with id: " << id << " has incorrect miner transaction");
bvc.m_verifivation_failed = true; bvc.m_verifivation_failed = true;
@ -3651,11 +3720,11 @@ leave:
} }
TIME_MEASURE_FINISH(vmt); TIME_MEASURE_FINISH(vmt);
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
// populate various metadata about the block to be stored alongside it. // populate various metadata about the block to be stored alongside it.
block_size = cumulative_block_size; block_weight = cumulative_block_weight;
cumulative_difficulty = current_diffic; cumulative_difficulty = current_diffic;
// In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of
// coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins
@ -3676,7 +3745,7 @@ leave:
{ {
try try
{ {
new_height = m_db->add_block(bl, block_size, cumulative_difficulty, already_generated_coins, txs); new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs);
} }
catch (const KEY_IMAGE_EXISTS& e) catch (const KEY_IMAGE_EXISTS& e)
{ {
@ -3701,14 +3770,14 @@ leave:
TIME_MEASURE_FINISH(addblock); TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the size limit may change due to fork // do this after updating the hard fork state since the weight limit may change due to fork
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms");
if(m_show_time_stats) if(m_show_time_stats)
{ {
MINFO("Height: " << new_height << " blob: " << coinbase_blob_size << " cumm: " MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: "
<< cumulative_block_size << " p/t: " << block_processing_time << " (" << cumulative_block_weight << " p/t: " << block_processing_time << " ("
<< target_calculating_time << "/" << longhash_calculating_time << "/" << target_calculating_time << "/" << longhash_calculating_time << "/"
<< t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool << t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool
<< "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms"); << "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms");
@ -3725,20 +3794,20 @@ leave:
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::update_next_cumulative_size_limit() bool Blockchain::update_next_cumulative_weight_limit()
{ {
uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version()); uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version());
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
std::vector<size_t> sz; std::vector<size_t> weights;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
uint64_t median = epee::misc_utils::median(sz); uint64_t median = epee::misc_utils::median(weights);
m_current_block_cumul_sz_median = median; m_current_block_cumul_weight_median = median;
if(median <= full_reward_zone) if(median <= full_reward_zone)
median = full_reward_zone; median = full_reward_zone;
m_current_block_cumul_sz_limit = median*2; m_current_block_cumul_weight_limit = median*2;
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -4635,14 +4704,14 @@ void Blockchain::load_compiled_in_block_hashes()
std::vector<transaction> txs; std::vector<transaction> txs;
m_tx_pool.get_transactions(txs); m_tx_pool.get_transactions(txs);
size_t blob_size; size_t tx_weight;
uint64_t fee; uint64_t fee;
bool relayed, do_not_relay, double_spend_seen; bool relayed, do_not_relay, double_spend_seen;
transaction pool_tx; transaction pool_tx;
for(const transaction &tx : txs) for(const transaction &tx : txs)
{ {
crypto::hash tx_hash = get_transaction_hash(tx); crypto::hash tx_hash = get_transaction_hash(tx);
m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay, double_spend_seen); m_tx_pool.take_tx(tx_hash, pool_tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen);
} }
} }
} }

View File

@ -95,7 +95,7 @@ namespace cryptonote
{ {
block bl; //!< the block block bl; //!< the block
uint64_t height; //!< the height of the block in the blockchain uint64_t height; //!< the height of the block in the blockchain
size_t block_cumulative_size; //!< the size (in bytes) of the block size_t block_cumulative_weight; //!< the weight of the block
difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
uint64_t already_generated_coins; //!< the total coins minted after that block uint64_t already_generated_coins; //!< the total coins minted after that block
}; };
@ -579,46 +579,57 @@ namespace cryptonote
bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
/** /**
* @brief get dynamic per kB fee for a given block size * @brief get fee quantization mask
* *
* The dynamic fee is based on the block size in a past window, and * The dynamic fee may be quantized, to mask out the last decimal places
* the current block reward. It is expressed by kB.
* *
* @param block_reward the current block reward * @return the fee quantized mask
* @param median_block_size the median blob's size in the past window
* @param version hard fork version for rules and constants to use
*
* @return the per kB fee
*/ */
static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version); static uint64_t get_fee_quantization_mask();
/** /**
* @brief get dynamic per kB fee estimate for the next few blocks * @brief get dynamic per kB or byte fee for a given block weight
* *
* The dynamic fee is based on the block size in a past window, and * The dynamic fee is based on the block weight in a past window, and
* the current block reward. It is expressed by kB. This function * the current block reward. It is expressed by kB before v8, and
* calculates an estimate for a dynamic fee which will be valid for * per byte from v8.
* the next grace_blocks *
* @param block_reward the current block reward
* @param median_block_weight the median block weight in the past window
* @param version hard fork version for rules and constants to use
*
* @return the fee
*/
static uint64_t get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version);
/**
* @brief get dynamic per kB or byte fee estimate for the next few blocks
*
* The dynamic fee is based on the block weight in a past window, and
* the current block reward. It is expressed by kB before v8, and
* per byte from v8.
* This function calculates an estimate for a dynamic fee which will be
* valid for the next grace_blocks
* *
* @param grace_blocks number of blocks we want the fee to be valid for * @param grace_blocks number of blocks we want the fee to be valid for
* *
* @return the per kB fee estimate * @return the fee estimate
*/ */
uint64_t get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const; uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const;
/** /**
* @brief validate a transaction's fee * @brief validate a transaction's fee
* *
* This function validates the fee is enough for the transaction. * This function validates the fee is enough for the transaction.
* This is based on the size of the transaction blob, and, after a * This is based on the weight of the transaction, and, after a
* height threshold, on the average size of transaction in a past window * height threshold, on the average weight of transaction in a past window
* *
* @param blob_size the transaction blob's size * @param tx_weight the transaction weight
* @param fee the fee * @param fee the fee
* *
* @return true if the fee is enough, false otherwise * @return true if the fee is enough, false otherwise
*/ */
bool check_fee(size_t blob_size, uint64_t fee) const; bool check_fee(size_t tx_weight, uint64_t fee) const;
/** /**
* @brief check that a transaction's outputs conform to current standards * @brief check that a transaction's outputs conform to current standards
@ -635,18 +646,18 @@ namespace cryptonote
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc); bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc);
/** /**
* @brief gets the blocksize limit based on recent blocks * @brief gets the block weight limit based on recent blocks
* *
* @return the limit * @return the limit
*/ */
uint64_t get_current_cumulative_blocksize_limit() const; uint64_t get_current_cumulative_block_weight_limit() const;
/** /**
* @brief gets the blocksize median based on recent blocks (same window as for the limit) * @brief gets the block weight median based on recent blocks (same window as for the limit)
* *
* @return the median * @return the median
*/ */
uint64_t get_current_cumulative_blocksize_median() const; uint64_t get_current_cumulative_block_weight_median() const;
/** /**
* @brief gets the difficulty of the block with a given height * @brief gets the difficulty of the block with a given height
@ -1001,8 +1012,8 @@ namespace cryptonote
// main chain // main chain
transactions_container m_transactions; transactions_container m_transactions;
size_t m_current_block_cumul_sz_limit; size_t m_current_block_cumul_weight_limit;
size_t m_current_block_cumul_sz_median; size_t m_current_block_cumul_weight_median;
// metadata containers // metadata containers
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table; std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table;
@ -1225,7 +1236,7 @@ namespace cryptonote
* and that his miner transaction totals reward + fee. * and that his miner transaction totals reward + fee.
* *
* @param b the block containing the miner transaction to be validated * @param b the block containing the miner transaction to be validated
* @param cumulative_block_size the block's size * @param cumulative_block_weight the block's weight
* @param fee the total fees collected in the block * @param fee the total fees collected in the block
* @param base_reward return-by-reference the new block's generated coins * @param base_reward return-by-reference the new block's generated coins
* @param already_generated_coins the amount of currency generated prior to this block * @param already_generated_coins the amount of currency generated prior to this block
@ -1234,7 +1245,7 @@ namespace cryptonote
* *
* @return false if anything is found wrong with the miner transaction, otherwise true * @return false if anything is found wrong with the miner transaction, otherwise true
*/ */
bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version); bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version);
/** /**
* @brief reverts the blockchain to its previous state following a failed switch * @brief reverts the blockchain to its previous state following a failed switch
@ -1251,14 +1262,14 @@ namespace cryptonote
bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height); bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height);
/** /**
* @brief gets recent block sizes for median calculation * @brief gets recent block weights for median calculation
* *
* get the block sizes of the last <count> blocks, and return by reference <sz>. * get the block weights of the last <count> blocks, and return by reference <sz>.
* *
* @param sz return-by-reference the list of sizes * @param sz return-by-reference the list of weights
* @param count the number of blocks to get sizes for * @param count the number of blocks to get weights for
*/ */
void get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const; void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
/** /**
* @brief adds the given output to the requested set of random outputs * @brief adds the given output to the requested set of random outputs
@ -1373,11 +1384,11 @@ namespace cryptonote
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps); bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
/** /**
* @brief calculate the block size limit for the next block to be added * @brief calculate the block weight limit for the next block to be added
* *
* @return true * @return true
*/ */
bool update_next_cumulative_size_limit(); bool update_next_cumulative_weight_limit();
void return_tx_to_pool(std::vector<transaction> &txs); void return_tx_to_pool(std::vector<transaction> &txs);
/** /**

View File

@ -162,10 +162,10 @@ namespace cryptonote
, "Relay blocks as normal blocks" , "Relay blocks as normal blocks"
, false , false
}; };
static const command_line::arg_descriptor<size_t> arg_max_txpool_size = { static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-size" "max-txpool-weight"
, "Set maximum txpool size in bytes." , "Set maximum txpool weight in bytes."
, DEFAULT_TXPOOL_MAX_SIZE , DEFAULT_TXPOOL_MAX_WEIGHT
}; };
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -274,7 +274,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_test_dbg_lock_sleep); command_line::add_arg(desc, arg_test_dbg_lock_sleep);
command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_max_txpool_size); command_line::add_arg(desc, arg_max_txpool_weight);
miner::init_options(desc); miner::init_options(desc);
BlockchainDB::init_options(desc); BlockchainDB::init_options(desc);
@ -402,7 +402,7 @@ namespace cryptonote
bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0;
uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads); uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads);
std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
size_t max_txpool_size = command_line::get_arg(vm, arg_max_txpool_size); size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
boost::filesystem::path folder(m_config_folder); boost::filesystem::path folder(m_config_folder);
if (m_nettype == FAKECHAIN) if (m_nettype == FAKECHAIN)
@ -551,7 +551,7 @@ namespace cryptonote
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); 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 ? &regtest_test_options : test_options, fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
r = m_mempool.init(max_txpool_size); r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
// now that we have a valid m_blockchain_storage, we can clean out any // now that we have a valid m_blockchain_storage, we can clean out any
@ -692,14 +692,12 @@ namespace cryptonote
return false; return false;
} }
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area()) return true;
{
MTRACE("Skipping semantics check for tx kept by block in embedded hash area");
} }
else if(!check_tx_semantic(tx, keeped_by_block)) //-----------------------------------------------------------------------------------------------
void core::set_semantics_failed(const crypto::hash &tx_hash)
{ {
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
tvc.m_verifivation_failed = true;
bad_semantics_txes_lock.lock(); bad_semantics_txes_lock.lock();
bad_semantics_txes[0].insert(tx_hash); bad_semantics_txes[0].insert(tx_hash);
if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE)
@ -708,10 +706,109 @@ namespace cryptonote
bad_semantics_txes[0].clear(); bad_semantics_txes[0].clear();
} }
bad_semantics_txes_lock.unlock(); bad_semantics_txes_lock.unlock();
}
//-----------------------------------------------------------------------------------------------
static bool is_canonical_bulletproof_layout(const std::vector<rct::Bulletproof> &proofs)
{
if (proofs.size() != 1)
return false; return false;
const size_t sz = proofs[0].V.size();
if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
{
bool ret = true;
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
{
MTRACE("Skipping semantics check for tx kept by block in embedded hash area");
return true;
} }
return true; std::vector<const rct::rctSig*> rvv;
for (size_t n = 0; n < tx_info.size(); ++n)
{
if (!check_tx_semantic(*tx_info[n].tx, keeped_by_block))
{
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
continue;
}
if (tx_info[n].tx->version < 2)
continue;
const rct::rctSig &rv = tx_info[n].tx->rct_signatures;
switch (rv.type) {
case rct::RCTTypeNull:
// coinbase should not come here, so we reject for all other types
MERROR_VER("Unexpected Null rctSig type");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
case rct::RCTTypeSimple:
if (!rct::verRctSemanticsSimple(rv))
{
MERROR_VER("rct signature semantics check failed");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
}
break;
case rct::RCTTypeFull:
if (!rct::verRct(rv, true))
{
MERROR_VER("rct signature semantics check failed");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
}
break;
case rct::RCTTypeBulletproof:
if (!is_canonical_bulletproof_layout(rv.p.bulletproofs))
{
MERROR_VER("Bulletproof does not have canonical form");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
}
rvv.push_back(&rv); // delayed batch verification
break;
default:
MERROR_VER("Unknown rct type: " << rv.type);
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
}
}
if (!rvv.empty() && !rct::verRctSemanticsSimple(rvv))
{
LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time");
ret = false;
const bool assumed_bad = rvv.size() == 1; // if there's only one tx, it must be the bad one
for (size_t n = 0; n < tx_info.size(); ++n)
{
if (!tx_info[n].result)
continue;
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof)
continue;
if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures))
{
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
}
}
}
return ret;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) bool core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
@ -769,6 +866,16 @@ namespace cryptonote
} }
waiter.wait(&tpool); waiter.wait(&tpool);
std::vector<tx_verification_batch_info> tx_info;
tx_info.reserve(tx_blobs.size());
for (size_t i = 0; i < tx_blobs.size(); i++) {
if (!results[i].res)
continue;
tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res});
}
if (!tx_info.empty())
handle_incoming_tx_accumulated_batch(tx_info, keeped_by_block);
bool ok = true; bool ok = true;
it = tx_blobs.begin(); it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
@ -778,7 +885,8 @@ namespace cryptonote
continue; continue;
} }
ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, it->size(), tvc[i], keeped_by_block, relayed, do_not_relay); const size_t weight = get_transaction_weight(results[i].tx, it->size());
ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay);
if(tvc[i].m_verifivation_failed) if(tvc[i].m_verifivation_failed)
{MERROR_VER("Transaction verification failed: " << results[i].hash);} {MERROR_VER("Transaction verification failed: " << results[i].hash);}
else if(tvc[i].m_verifivation_impossible) else if(tvc[i].m_verifivation_impossible)
@ -861,9 +969,9 @@ namespace cryptonote
} }
// for version > 1, ringct signatures check verifies amounts match // for version > 1, ringct signatures check verifies amounts match
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) if(!keeped_by_block && get_transaction_weight(tx) >= m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
{ {
MERROR_VER("tx is too large " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); MERROR_VER("tx is too large " << get_transaction_weight(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
return false; return false;
} }
@ -886,36 +994,6 @@ namespace cryptonote
return false; return false;
} }
if (tx.version >= 2)
{
const rct::rctSig &rv = tx.rct_signatures;
switch (rv.type) {
case rct::RCTTypeNull:
// coinbase should not come here, so we reject for all other types
MERROR_VER("Unexpected Null rctSig type");
return false;
case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
if (!rct::verRctSimple(rv, true))
{
MERROR_VER("rct signature semantics check failed");
return false;
}
break;
case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
if (!rct::verRct(rv, true))
{
MERROR_VER("rct signature semantics check failed");
return false;
}
break;
default:
MERROR_VER("Unknown rct type: " << rv.type);
return false;
}
}
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -1025,7 +1103,8 @@ namespace cryptonote
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
blobdata bl; blobdata bl;
t_serializable_object_to_blob(tx, bl); t_serializable_object_to_blob(tx, bl);
return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block, relayed, do_not_relay); size_t tx_weight = get_transaction_weight(tx, bl.size());
return add_new_tx(tx, tx_hash, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
size_t core::get_blockchain_total_transactions() const size_t core::get_blockchain_total_transactions() const
@ -1033,7 +1112,7 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions(); return m_blockchain_storage.get_total_transactions();
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{ {
if (keeped_by_block) if (keeped_by_block)
get_blockchain_storage().on_new_tx_from_block(tx); get_blockchain_storage().on_new_tx_from_block(tx);
@ -1051,7 +1130,7 @@ namespace cryptonote
} }
uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); return m_mempool.add_tx(tx, tx_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::relay_txpool_transactions() bool core::relay_txpool_transactions()

View File

@ -788,12 +788,12 @@ namespace cryptonote
* *
* @param tx_hash the transaction's hash * @param tx_hash the transaction's hash
* @param tx_prefix_hash the transaction prefix' hash * @param tx_prefix_hash the transaction prefix' hash
* @param blob_size the size of the transaction * @param tx_weight the weight of the transaction
* @param relayed whether or not the transaction was relayed to us * @param relayed whether or not the transaction was relayed to us
* @param do_not_relay whether to prevent the transaction from being relayed * @param do_not_relay whether to prevent the transaction from being relayed
* *
*/ */
bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
/** /**
* @brief add a new transaction to the transaction pool * @brief add a new transaction to the transaction pool
@ -864,9 +864,12 @@ namespace cryptonote
* @return true if all the checks pass, otherwise false * @return true if all the checks pass, otherwise false
*/ */
bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const;
void set_semantics_failed(const crypto::hash &tx_hash);
bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay);
bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay);
struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; };
bool handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block);
/** /**
* @copydoc miner::on_block_chain_update * @copydoc miner::on_block_chain_update

View File

@ -74,7 +74,7 @@ namespace cryptonote
LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses");
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.extra.clear(); tx.extra.clear();
@ -89,7 +89,7 @@ namespace cryptonote
in.height = height; in.height = height;
uint64_t block_reward; uint64_t block_reward;
if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version))
{ {
LOG_PRINT_L0("Block is too big"); LOG_PRINT_L0("Block is too big");
return false; return false;
@ -195,7 +195,7 @@ namespace cryptonote
return addr.m_view_public_key; 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, bool bulletproof, 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, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs)
{ {
hw::device &hwdev = sender_account_keys.get_device(); hw::device &hwdev = sender_account_keys.get_device();
@ -491,7 +491,7 @@ namespace cryptonote
// the non-simple version is slightly smaller, but assumes all real inputs // 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. // are on the same index, so can only be used if there just one ring.
bool use_simple_rct = sources.size() > 1; bool use_simple_rct = sources.size() > 1 || range_proof_type != rct::RangeProofBorromean;
if (!use_simple_rct) if (!use_simple_rct)
{ {
@ -589,9 +589,9 @@ namespace cryptonote
get_transaction_prefix_hash(tx, tx_prefix_hash); get_transaction_prefix_hash(tx, tx_prefix_hash);
rct::ctkeyV outSk; rct::ctkeyV outSk;
if (use_simple_rct) 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, bulletproof, 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, range_proof_type, hwdev);
else 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, bulletproof, 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, hwdev); // same index assumption
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
@ -604,7 +604,7 @@ namespace cryptonote
return true; 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, bool bulletproof, 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, rct::RangeProofType range_proof_type, rct::multisig_out *msout)
{ {
hw::device &hwdev = sender_account_keys.get_device(); hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key); hwdev.open_tx(tx_key);
@ -622,7 +622,7 @@ namespace cryptonote
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec); 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, bulletproof, 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, range_proof_type, msout);
hwdev.close_tx(); hwdev.close_tx();
return r; return r;
} }
@ -634,7 +634,7 @@ namespace cryptonote
crypto::secret_key tx_key; crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
std::vector<tx_destination_entry> destinations_copy = destinations; 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, false, 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, NULL);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool generate_genesis_block( bool generate_genesis_block(

View File

@ -37,7 +37,7 @@
namespace cryptonote namespace cryptonote
{ {
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
struct tx_source_entry struct tx_source_entry
{ {
@ -90,8 +90,8 @@ namespace cryptonote
//--------------------------------------------------------------- //---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr); 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(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, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true); 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, bool bulletproof = false, rct::multisig_out *msout = NULL); bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, 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 generate_genesis_block( bool generate_genesis_block(
block& bl block& bl

View File

@ -80,9 +80,13 @@ namespace cryptonote
return amount * ACCEPT_THRESHOLD; return amount * ACCEPT_THRESHOLD;
} }
uint64_t get_transaction_size_limit(uint8_t version) uint64_t get_transaction_weight_limit(uint8_t version)
{ {
return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; // from v8, limit a tx to 50% of the minimum block weight
if (version >= 8)
return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
else
return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
} }
// This class is meant to create a batch when none currently exists. // This class is meant to create a batch when none currently exists.
@ -102,12 +106,12 @@ namespace cryptonote
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0) tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0)
{ {
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version)
{ {
// this should already be called with that lock, but let's make it explicit for clarity // this should already be called with that lock, but let's make it explicit for clarity
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
@ -173,17 +177,17 @@ namespace cryptonote
fee = tx.rct_signatures.txnFee; fee = tx.rct_signatures.txnFee;
} }
if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee)) if (!kept_by_block && !m_blockchain.check_fee(tx_weight, fee))
{ {
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
tvc.m_fee_too_low = true; tvc.m_fee_too_low = true;
return false; return false;
} }
size_t tx_size_limit = get_transaction_size_limit(version); size_t tx_weight_limit = get_transaction_weight_limit(version);
if (!kept_by_block && blob_size > tx_size_limit) if (!kept_by_block && tx_weight > tx_weight_limit)
{ {
LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit); LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit);
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
tvc.m_too_big = true; tvc.m_too_big = true;
return false; return false;
@ -227,7 +231,7 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check. // may become valid again, so ignore the failed inputs check.
if(kept_by_block) if(kept_by_block)
{ {
meta.blob_size = blob_size; meta.weight = tx_weight;
meta.fee = fee; meta.fee = fee;
meta.max_used_block_id = null_hash; meta.max_used_block_id = null_hash;
meta.max_used_block_height = 0; meta.max_used_block_height = 0;
@ -248,7 +252,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta); m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block)) if (!insert_key_images(tx, kept_by_block))
return false; return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -267,7 +271,7 @@ namespace cryptonote
}else }else
{ {
//update transactions container //update transactions container
meta.blob_size = blob_size; meta.weight = tx_weight;
meta.kept_by_block = kept_by_block; meta.kept_by_block = kept_by_block;
meta.fee = fee; meta.fee = fee;
meta.max_used_block_id = max_used_block_id; meta.max_used_block_id = max_used_block_id;
@ -290,7 +294,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta); m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block)) if (!insert_key_images(tx, kept_by_block))
return false; return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -304,13 +308,13 @@ namespace cryptonote
} }
tvc.m_verifivation_failed = false; tvc.m_verifivation_failed = false;
m_txpool_size += blob_size; m_txpool_weight += tx_weight;
++m_cookie; ++m_cookie;
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight));
prune(m_txpool_max_size); prune(m_txpool_max_weight);
return true; return true;
} }
@ -321,26 +325,26 @@ namespace cryptonote
size_t blob_size = 0; size_t blob_size = 0;
if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0) if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0)
return false; return false;
return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); return add_tx(tx, h, get_transaction_weight(tx, blob_size), tvc, keeped_by_block, relayed, do_not_relay, version);
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
size_t tx_memory_pool::get_txpool_size() const size_t tx_memory_pool::get_txpool_weight() const
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
return m_txpool_size; return m_txpool_weight;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void tx_memory_pool::set_txpool_max_size(size_t bytes) void tx_memory_pool::set_txpool_max_weight(size_t bytes)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
m_txpool_max_size = bytes; m_txpool_max_weight = bytes;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void tx_memory_pool::prune(size_t bytes) void tx_memory_pool::prune(size_t bytes)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
if (bytes == 0) if (bytes == 0)
bytes = m_txpool_max_size; bytes = m_txpool_max_weight;
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
bool changed = false; bool changed = false;
@ -349,7 +353,7 @@ namespace cryptonote
auto it = --m_txs_by_fee_and_receive_time.end(); auto it = --m_txs_by_fee_and_receive_time.end();
while (it != m_txs_by_fee_and_receive_time.begin()) while (it != m_txs_by_fee_and_receive_time.begin())
{ {
if (m_txpool_size <= bytes) if (m_txpool_weight <= bytes)
break; break;
try try
{ {
@ -374,11 +378,11 @@ namespace cryptonote
return; return;
} }
// remove first, in case this throws, so key images aren't removed // remove first, in case this throws, so key images aren't removed
MINFO("Pruning tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= txblob.size(); m_txpool_weight -= it->first.second;
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--); m_txs_by_fee_and_receive_time.erase(it--);
changed = true; changed = true;
} }
@ -390,8 +394,8 @@ namespace cryptonote
} }
if (changed) if (changed)
++m_cookie; ++m_cookie;
if (m_txpool_size > bytes) if (m_txpool_weight > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes);
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block) bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block)
@ -446,7 +450,7 @@ namespace cryptonote
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
@ -470,7 +474,7 @@ namespace cryptonote
MERROR("Failed to parse tx from txpool"); MERROR("Failed to parse tx from txpool");
return false; return false;
} }
blob_size = meta.blob_size; tx_weight = meta.weight;
fee = meta.fee; fee = meta.fee;
relayed = meta.relayed; relayed = meta.relayed;
do_not_relay = meta.do_not_relay; do_not_relay = meta.do_not_relay;
@ -478,7 +482,7 @@ namespace cryptonote
// remove first, in case this throws, so key images aren't removed // remove first, in case this throws, so key images aren't removed
m_blockchain.remove_txpool_tx(id); m_blockchain.remove_txpool_tx(id);
m_txpool_size -= blob_size; m_txpool_weight -= tx_weight;
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
} }
catch (const std::exception &e) catch (const std::exception &e)
@ -552,7 +556,7 @@ namespace cryptonote
{ {
// remove first, so we only remove key images if the tx removal succeeds // remove first, so we only remove key images if the tx removal succeeds
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= bd.size(); m_txpool_weight -= get_transaction_weight(tx, bd.size());
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
} }
} }
@ -670,7 +674,7 @@ namespace cryptonote
const uint64_t now = time(NULL); const uint64_t now = time(NULL);
backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); backlog.push_back({meta.weight, meta.fee, meta.receive_time - now});
return true; return true;
}, false, include_unrelayed_txes); }, false, include_unrelayed_txes);
} }
@ -682,15 +686,15 @@ namespace cryptonote
const uint64_t now = time(NULL); const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes; std::map<uint64_t, txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
std::vector<uint32_t> sizes; std::vector<uint32_t> weights;
sizes.reserve(stats.txs_total); weights.reserve(stats.txs_total);
m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
sizes.push_back(meta.blob_size); weights.push_back(meta.weight);
stats.bytes_total += meta.blob_size; stats.bytes_total += meta.weight;
if (!stats.bytes_min || meta.blob_size < stats.bytes_min) if (!stats.bytes_min || meta.weight < stats.bytes_min)
stats.bytes_min = meta.blob_size; stats.bytes_min = meta.weight;
if (meta.blob_size > stats.bytes_max) if (meta.weight > stats.bytes_max)
stats.bytes_max = meta.blob_size; stats.bytes_max = meta.weight;
if (!meta.relayed) if (!meta.relayed)
stats.num_not_relayed++; stats.num_not_relayed++;
stats.fee_total += meta.fee; stats.fee_total += meta.fee;
@ -702,12 +706,12 @@ namespace cryptonote
stats.num_failing++; stats.num_failing++;
uint64_t age = now - meta.receive_time + (now == meta.receive_time); uint64_t age = now - meta.receive_time + (now == meta.receive_time);
agebytes[age].txs++; agebytes[age].txs++;
agebytes[age].bytes += meta.blob_size; agebytes[age].bytes += meta.weight;
if (meta.double_spend_seen) if (meta.double_spend_seen)
++stats.num_double_spends; ++stats.num_double_spends;
return true; return true;
}, false, include_unrelayed_txes); }, false, include_unrelayed_txes);
stats.bytes_med = epee::misc_utils::median(sizes); stats.bytes_med = epee::misc_utils::median(weights);
if (stats.txs_total > 1) if (stats.txs_total > 1)
{ {
/* looking for 98th percentile */ /* looking for 98th percentile */
@ -771,7 +775,8 @@ namespace cryptonote
return true; return true;
} }
txi.tx_json = obj_to_json_str(tx); txi.tx_json = obj_to_json_str(tx);
txi.blob_size = meta.blob_size; txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee; txi.fee = meta.fee;
txi.kept_by_block = meta.kept_by_block; txi.kept_by_block = meta.kept_by_block;
txi.max_used_block_height = meta.max_used_block_height; txi.max_used_block_height = meta.max_used_block_height;
@ -842,7 +847,8 @@ namespace cryptonote
return true; return true;
} }
txi.tx = tx; txi.tx = tx;
txi.blob_size = meta.blob_size; txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee; txi.fee = meta.fee;
txi.kept_by_block = meta.kept_by_block; txi.kept_by_block = meta.kept_by_block;
txi.max_used_block_height = meta.max_used_block_height; txi.max_used_block_height = meta.max_used_block_height;
@ -1116,7 +1122,8 @@ namespace cryptonote
} }
ss << obj_to_json_str(tx) << std::endl; ss << obj_to_json_str(tx) << std::endl;
} }
ss << "blob_size: " << meta.blob_size << std::endl ss << "blob_size: " << (short_format ? "-" : std::to_string(txblob->size())) << std::endl
<< "weight: " << meta.weight << std::endl
<< "fee: " << print_money(meta.fee) << std::endl << "fee: " << print_money(meta.fee) << std::endl
<< "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl
<< "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl
@ -1131,25 +1138,25 @@ namespace cryptonote
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
uint64_t best_coinbase = 0, coinbase = 0; uint64_t best_coinbase = 0, coinbase = 0;
total_size = 0; total_weight = 0;
fee = 0; fee = 0;
//baseline empty block //baseline empty block
get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version);
size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5; size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
std::unordered_set<crypto::key_image> k_images; std::unordered_set<crypto::key_image> k_images;
LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
@ -1162,12 +1169,12 @@ namespace cryptonote
MERROR(" failed to find tx meta"); MERROR(" failed to find tx meta");
continue; continue;
} }
LOG_PRINT_L2("Considering " << sorted_it->second << ", size " << meta.blob_size << ", current block size " << total_size << "/" << max_total_size << ", current coinbase " << print_money(best_coinbase)); LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase));
// Can not exceed maximum block size // Can not exceed maximum block weight
if (max_total_size < total_size + meta.blob_size) if (max_total_weight < total_weight + meta.weight)
{ {
LOG_PRINT_L2(" would exceed maximum block size"); LOG_PRINT_L2(" would exceed maximum block weight");
continue; continue;
} }
@ -1177,9 +1184,9 @@ namespace cryptonote
// If we're getting lower coinbase tx, // If we're getting lower coinbase tx,
// stop including more tx // stop including more tx
uint64_t block_reward; uint64_t block_reward;
if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version)) if(!get_block_reward(median_weight, total_weight + meta.weight, already_generated_coins, block_reward, version))
{ {
LOG_PRINT_L2(" would exceed maximum block size"); LOG_PRINT_L2(" would exceed maximum block weight");
continue; continue;
} }
coinbase = block_reward + fee + meta.fee; coinbase = block_reward + fee + meta.fee;
@ -1191,11 +1198,11 @@ namespace cryptonote
} }
else else
{ {
// If we've exceeded the penalty free size, // If we've exceeded the penalty free weight,
// stop including more tx // stop including more tx
if (total_size > median_size) if (total_weight > median_weight)
{ {
LOG_PRINT_L2(" would exceed median block size"); LOG_PRINT_L2(" would exceed median block weight");
break; break;
} }
} }
@ -1241,16 +1248,16 @@ namespace cryptonote
} }
bl.tx_hashes.push_back(sorted_it->second); bl.tx_hashes.push_back(sorted_it->second);
total_size += meta.blob_size; total_weight += meta.weight;
fee += meta.fee; fee += meta.fee;
best_coinbase = coinbase; best_coinbase = coinbase;
append_key_images(k_images, tx); append_key_images(k_images, tx);
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); LOG_PRINT_L2(" added, new block weight " << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase));
} }
expected_reward = best_coinbase; expected_reward = best_coinbase;
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight "
<< total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase)
<< " (including " << print_money(fee) << " in fees)"); << " (including " << print_money(fee) << " in fees)");
return true; return true;
} }
@ -1259,14 +1266,14 @@ namespace cryptonote
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
size_t tx_size_limit = get_transaction_size_limit(version); size_t tx_weight_limit = get_transaction_weight_limit(version);
std::unordered_set<crypto::hash> remove; std::unordered_set<crypto::hash> remove;
m_txpool_size = 0; m_txpool_weight = 0;
m_blockchain.for_all_txpool_txes([this, &remove, tx_size_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { m_blockchain.for_all_txpool_txes([this, &remove, tx_weight_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) {
m_txpool_size += meta.blob_size; m_txpool_weight += meta.weight;
if (meta.blob_size > tx_size_limit) { if (meta.weight > tx_weight_limit) {
LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.blob_size << " bytes), removing it from pool"); LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.weight << " bytes), removing it from pool");
remove.insert(txid); remove.insert(txid);
} }
else if (m_blockchain.have_tx(txid)) { else if (m_blockchain.have_tx(txid)) {
@ -1293,7 +1300,7 @@ namespace cryptonote
} }
// remove tx from db first // remove tx from db first
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= txblob.size(); m_txpool_weight -= get_transaction_weight(tx, txblob.size());
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
auto sorted_it = find_tx_in_sorted_container(txid); auto sorted_it = find_tx_in_sorted_container(txid);
if (sorted_it == m_txs_by_fee_and_receive_time.end()) if (sorted_it == m_txs_by_fee_and_receive_time.end())
@ -1318,15 +1325,15 @@ namespace cryptonote
return n_removed; return n_removed;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::init(size_t max_txpool_size) bool tx_memory_pool::init(size_t max_txpool_weight)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
m_txpool_max_size = max_txpool_size ? max_txpool_size : DEFAULT_TXPOOL_MAX_SIZE; m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_TXPOOL_MAX_WEIGHT;
m_txs_by_fee_and_receive_time.clear(); m_txs_by_fee_and_receive_time.clear();
m_spent_key_images.clear(); m_spent_key_images.clear();
m_txpool_size = 0; m_txpool_weight = 0;
std::vector<crypto::hash> remove; std::vector<crypto::hash> remove;
// first add the not kept by block, then the kept by block, // first add the not kept by block, then the kept by block,
@ -1348,8 +1355,8 @@ namespace cryptonote
MFATAL("Failed to insert key images from txpool tx"); MFATAL("Failed to insert key images from txpool tx");
return false; return false;
} }
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid); m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid);
m_txpool_size += meta.blob_size; m_txpool_weight += meta.weight;
return true; return true;
}, true); }, true);
if (!r) if (!r)

View File

@ -84,7 +84,7 @@ namespace cryptonote
* *
* This handling includes: * This handling includes:
* storing the transactions * storing the transactions
* organizing the transactions by fee per size * organizing the transactions by fee per weight unit
* taking/giving transactions to and from various other components * taking/giving transactions to and from various other components
* saving the transactions to disk on shutdown * saving the transactions to disk on shutdown
* helping create a new block template by choosing transactions for it * helping create a new block template by choosing transactions for it
@ -105,9 +105,9 @@ namespace cryptonote
* @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t) * @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t)
* *
* @param id the transaction's hash * @param id the transaction's hash
* @param blob_size the transaction's size * @param tx_weight the transaction's weight
*/ */
bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); bool add_tx(transaction &tx, const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version);
/** /**
* @brief add a transaction to the transaction pool * @brief add a transaction to the transaction pool
@ -133,7 +133,7 @@ namespace cryptonote
* *
* @param id the hash of the transaction * @param id the hash of the transaction
* @param tx return-by-reference the transaction taken * @param tx return-by-reference the transaction taken
* @param blob_size return-by-reference the transaction's size * @param tx_weight return-by-reference the transaction's weight
* @param fee the transaction fee * @param fee the transaction fee
* @param relayed return-by-reference was transaction relayed to us by the network? * @param relayed return-by-reference was transaction relayed to us by the network?
* @param do_not_relay return-by-reference is transaction not to be relayed to the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network?
@ -141,7 +141,7 @@ namespace cryptonote
* *
* @return true unless the transaction cannot be found in the pool * @return true unless the transaction cannot be found in the pool
*/ */
bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); bool take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen);
/** /**
* @brief checks if the pool has a transaction with the given hash * @brief checks if the pool has a transaction with the given hash
@ -198,11 +198,11 @@ namespace cryptonote
/** /**
* @brief loads pool state (if any) from disk, and initializes pool * @brief loads pool state (if any) from disk, and initializes pool
* *
* @param max_txpool_size the max size in bytes * @param max_txpool_weight the max weight in bytes
* *
* @return true * @return true
*/ */
bool init(size_t max_txpool_size = 0); bool init(size_t max_txpool_weight = 0);
/** /**
* @brief attempts to save the transaction pool state to disk * @brief attempts to save the transaction pool state to disk
@ -219,16 +219,16 @@ namespace cryptonote
* @brief Chooses transactions for a block to include * @brief Chooses transactions for a block to include
* *
* @param bl return-by-reference the block to fill in with transactions * @param bl return-by-reference the block to fill in with transactions
* @param median_size the current median block size * @param median_weight the current median block weight
* @param already_generated_coins the current total number of coins "minted" * @param already_generated_coins the current total number of coins "minted"
* @param total_size return-by-reference the total size of the new block * @param total_weight return-by-reference the total weight of the new block
* @param fee return-by-reference the total of fees from the included transactions * @param fee return-by-reference the total of fees from the included transactions
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param version hard fork version to use for consensus rules * @param version hard fork version to use for consensus rules
* *
* @return true * @return true
*/ */
bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
/** /**
* @brief get a list of all transactions in the pool * @brief get a list of all transactions in the pool
@ -249,7 +249,7 @@ namespace cryptonote
void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
/** /**
* @brief get (size, fee, receive time) for all transaction in the pool * @brief get (weight, fee, receive time) for all transaction in the pool
* *
* @param txs return-by-reference that data * @param txs return-by-reference that data
* @param include_unrelayed_txes include unrelayed txes in the result * @param include_unrelayed_txes include unrelayed txes in the result
@ -370,21 +370,21 @@ namespace cryptonote
uint64_t cookie() const { return m_cookie; } uint64_t cookie() const { return m_cookie; }
/** /**
* @brief get the cumulative txpool size in bytes * @brief get the cumulative txpool weight in bytes
* *
* @return the cumulative txpool size in bytes * @return the cumulative txpool weight in bytes
*/ */
size_t get_txpool_size() const; size_t get_txpool_weight() const;
/** /**
* @brief set the max cumulative txpool size in bytes * @brief set the max cumulative txpool weight in bytes
* *
* @param bytes the max cumulative txpool size in bytes * @param bytes the max cumulative txpool weight in bytes
*/ */
void set_txpool_max_size(size_t bytes); void set_txpool_max_weight(size_t bytes);
#define CURRENT_MEMPOOL_ARCHIVE_VER 11 #define CURRENT_MEMPOOL_ARCHIVE_VER 11
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12 #define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13
/** /**
* @brief information about a single transaction * @brief information about a single transaction
@ -393,6 +393,7 @@ namespace cryptonote
{ {
transaction tx; //!< the transaction transaction tx; //!< the transaction
size_t blob_size; //!< the transaction's size size_t blob_size; //!< the transaction's size
size_t weight; //!< the transaction's weight
uint64_t fee; //!< the transaction's fee amount uint64_t fee; //!< the transaction's fee amount
crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input
uint64_t max_used_block_height; //!< the height of the highest block referenced by an input uint64_t max_used_block_height; //!< the height of the highest block referenced by an input
@ -522,7 +523,7 @@ namespace cryptonote
/** /**
* @brief prune lowest fee/byte txes till we're not above bytes * @brief prune lowest fee/byte txes till we're not above bytes
* *
* if bytes is 0, use m_txpool_max_size * if bytes is 0, use m_txpool_max_weight
*/ */
void prune(size_t bytes = 0); void prune(size_t bytes = 0);
@ -578,8 +579,8 @@ private:
Blockchain& m_blockchain; //!< reference to the Blockchain object Blockchain& m_blockchain; //!< reference to the Blockchain object
size_t m_txpool_max_size; size_t m_txpool_max_weight;
size_t m_txpool_size; size_t m_txpool_weight;
mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache;
}; };
@ -608,6 +609,9 @@ namespace boost
if (version < 12) if (version < 12)
return; return;
ar & td.do_not_relay; ar & td.do_not_relay;
if (version < 13)
return;
ar & td.weight;
} }
} }
} }

View File

@ -76,6 +76,7 @@ namespace {
<< "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl << "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
<< "POW hash: " << header.pow_hash << std::endl << "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl << "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
<< "num txes: " << header.num_txes << std::endl << "num txes: " << header.num_txes << std::endl
<< "reward: " << cryptonote::print_money(header.reward); << "reward: " << cryptonote::print_money(header.reward);
} }
@ -558,7 +559,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
std::cout << std::endl; std::cout << std::endl;
std::cout std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl << "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
<< "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl; << "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
@ -857,8 +858,9 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< tx_info.tx_json << std::endl << tx_info.tx_json << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl << "blob_size: " << tx_info.blob_size << std::endl
<< "weight: " << tx_info.weight << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@ -940,8 +942,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
{ {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl << "blob_size: " << tx_info.blob_size << std::endl
<< "weight: " << tx_info.weight << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@ -996,7 +999,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0; size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0;
std::string backlog_message; std::string backlog_message;
const uint64_t full_reward_zone = ires.block_size_limit / 2; const uint64_t full_reward_zone = ires.block_weight_limit / 2;
if (res.pool_stats.bytes_total <= full_reward_zone) if (res.pool_stats.bytes_total <= full_reward_zone)
{ {
backlog_message = "no backlog"; backlog_message = "no backlog";
@ -1701,8 +1704,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
cryptonote::COMMAND_RPC_GET_INFO::response ires; cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request bhreq; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request bhreq;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request fereq; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response feres; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres;
epee::json_rpc::error error_resp; epee::json_rpc::error error_resp;
std::string fail_message = "Problem fetching info"; std::string fail_message = "Problem fetching info";
@ -1726,7 +1729,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
tools::fail_msg_writer() << make_error(fail_message, ires.status); tools::fail_msg_writer() << make_error(fail_message, ires.status);
return true; return true;
} }
if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) if (!m_rpc_server->on_get_base_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK)
{ {
tools::fail_msg_writer() << make_error(fail_message, feres.status); tools::fail_msg_writer() << make_error(fail_message, feres.status);
return true; return true;
@ -1762,8 +1765,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
double avgdiff = 0; double avgdiff = 0;
double avgnumtxes = 0; double avgnumtxes = 0;
double avgreward = 0; double avgreward = 0;
std::vector<uint64_t> sizes; std::vector<uint64_t> weights;
sizes.reserve(nblocks); weights.reserve(nblocks);
uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0; uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0;
std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0); std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0);
for (const auto &bhr: bhres.headers) for (const auto &bhr: bhres.headers)
@ -1771,7 +1774,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgdiff += bhr.difficulty; avgdiff += bhr.difficulty;
avgnumtxes += bhr.num_txes; avgnumtxes += bhr.num_txes;
avgreward += bhr.reward; avgreward += bhr.reward;
sizes.push_back(bhr.block_size); weights.push_back(bhr.block_weight);
static_assert(sizeof(bhr.major_version) == 1, "major_version expected to be uint8_t"); static_assert(sizeof(bhr.major_version) == 1, "major_version expected to be uint8_t");
static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t"); static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t");
major_versions[(unsigned)bhr.major_version]++; major_versions[(unsigned)bhr.major_version]++;
@ -1782,9 +1785,9 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgdiff /= nblocks; avgdiff /= nblocks;
avgnumtxes /= nblocks; avgnumtxes /= nblocks;
avgreward /= nblocks; avgreward /= nblocks;
uint64_t median_block_size = epee::misc_utils::median(sizes); uint64_t median_block_weight = epee::misc_utils::median(weights);
tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
<< ", avg. reward " << cryptonote::print_money(avgreward) << ", median block size " << median_block_size; << ", avg. reward " << cryptonote::print_money(avgreward) << ", median block weight " << median_block_weight;
unsigned int max_major = 256, max_minor = 256; unsigned int max_major = 256, max_minor = 256;
while (max_major > 0 && !major_versions[--max_major]); while (max_major > 0 && !major_versions[--max_major]);

View File

@ -1354,7 +1354,7 @@ namespace hw {
this->exchange(); this->exchange();
//pseudoOuts //pseudoOuts
if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeSimpleBulletproof)) { if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeBulletproof)) {
for ( i = 0; i < inputs_size; i++) { for ( i = 0; i < inputs_size; i++) {
offset = set_command_header(INS_VALIDATE, 0x01, i+2); offset = set_command_header(INS_VALIDATE, 0x01, i+2);
//options //options

View File

@ -30,11 +30,13 @@ set(ringct_basic_sources
rctOps.cpp rctOps.cpp
rctTypes.cpp rctTypes.cpp
rctCryptoOps.c rctCryptoOps.c
multiexp.cc
bulletproofs.cc) bulletproofs.cc)
set(ringct_basic_private_headers set(ringct_basic_private_headers
rctOps.h rctOps.h
rctTypes.h rctTypes.h
multiexp.h
bulletproofs.h) bulletproofs.h)
monero_private_headers(ringct_basic monero_private_headers(ringct_basic

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,11 @@ namespace rct
Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma); Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma);
Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma);
Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma);
Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &gamma);
bool bulletproof_VERIFY(const Bulletproof &proof); bool bulletproof_VERIFY(const Bulletproof &proof);
bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs);
bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs);
} }

665
src/ringct/multiexp.cc Normal file
View File

@ -0,0 +1,665 @@
// Copyright (c) 2017, 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.
//
// Adapted from Python code by Sarang Noether
#include "misc_log_ex.h"
#include "common/perf_timer.h"
extern "C"
{
#include "crypto/crypto-ops.h"
}
#include "common/aligned.h"
#include "rctOps.h"
#include "multiexp.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "multiexp"
//#define MULTIEXP_PERF(x) x
#define MULTIEXP_PERF(x)
#define RAW_MEMORY_BLOCK
//#define ALTERNATE_LAYOUT
//#define TRACK_STRAUS_ZERO_IDENTITY
// per points us for N/B points (B point bands)
// raw alt 128/192 4096/192 4096/4096
// 0 0 52.6 71 71.2
// 0 1 53.2 72.2 72.4
// 1 0 52.7 67 67.1
// 1 1 52.8 70.4 70.2
// Pippenger:
// 1 2 3 4 5 6 7 8 9 bestN
// 2 555 598 621 804 1038 1733 2486 5020 8304 1
// 4 783 747 800 1006 1428 2132 3285 5185 9806 2
// 8 1174 1071 1095 1286 1640 2398 3869 6378 12080 2
// 16 2279 1874 1745 1739 2144 2831 4209 6964 12007 4
// 32 3910 3706 2588 2477 2782 3467 4856 7489 12618 4
// 64 7184 5429 4710 4368 4010 4672 6027 8559 13684 5
// 128 14097 10574 8452 7297 6841 6718 8615 10580 15641 6
// 256 27715 20800 16000 13550 11875 11400 11505 14090 18460 6
// 512 55100 41250 31740 26570 22030 19830 20760 21380 25215 6
// 1024 111520 79000 61080 49720 43080 38320 37600 35040 36750 8
// 2048 219480 162680 122120 102080 83760 70360 66600 63920 66160 8
// 4096 453320 323080 247240 210200 180040 150240 132440 114920 110560 9
// 2 4 8 16 32 64 128 256 512 1024 2048 4096
// Bos Coster 858 994 1316 1949 3183 5512 9865 17830 33485 63160 124280 246320
// Straus 226 341 548 980 1870 3538 7039 14490 29020 57200 118640 233640
// Straus/cached 226 315 485 785 1514 2858 5753 11065 22970 45120 98880 194840
// Pippenger 555 747 1071 1739 2477 4010 6718 11400 19830 35040 63920 110560
// Best/cached Straus Straus Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip
// Best/uncached Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip Pip Pip
namespace rct
{
static inline bool operator<(const rct::key &k0, const rct::key&k1)
{
for (int n = 31; n >= 0; --n)
{
if (k0.bytes[n] < k1.bytes[n])
return true;
if (k0.bytes[n] > k1.bytes[n])
return false;
}
return false;
}
static inline rct::key div2(const rct::key &k)
{
rct::key res;
int carry = 0;
for (int n = 31; n >= 0; --n)
{
int new_carry = (k.bytes[n] & 1) << 7;
res.bytes[n] = k.bytes[n] / 2 + carry;
carry = new_carry;
}
return res;
}
static inline rct::key pow2(size_t n)
{
CHECK_AND_ASSERT_THROW_MES(n < 256, "Invalid pow2 argument");
rct::key res = rct::zero();
res[n >> 3] |= 1<<(n&7);
return res;
}
static inline int test(const rct::key &k, size_t n)
{
if (n >= 256) return 0;
return k[n >> 3] & (1 << (n & 7));
}
static inline void add(ge_p3 &p3, const ge_cached &other)
{
ge_p1p1 p1;
ge_add(&p1, &p3, &other);
ge_p1p1_to_p3(&p3, &p1);
}
static inline void add(ge_p3 &p3, const ge_p3 &other)
{
ge_cached cached;
ge_p3_to_cached(&cached, &other);
add(p3, cached);
}
rct::key bos_coster_heap_conv(std::vector<MultiexpData> data)
{
MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
size_t points = data.size();
CHECK_AND_ASSERT_THROW_MES(points > 1, "Not enough points");
std::vector<size_t> heap(points);
for (size_t n = 0; n < points; ++n)
heap[n] = n;
auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; };
std::make_heap(heap.begin(), heap.end(), Comp);
MULTIEXP_PERF(PERF_TIMER_STOP(setup));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(loop, 1000000));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(pop, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(pop));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(add, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(add));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(sub, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(sub));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(push, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(push));
while (heap.size() > 1)
{
MULTIEXP_PERF(PERF_TIMER_RESUME(pop));
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index1 = heap.back();
heap.pop_back();
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index2 = heap.back();
heap.pop_back();
MULTIEXP_PERF(PERF_TIMER_PAUSE(pop));
MULTIEXP_PERF(PERF_TIMER_RESUME(add));
ge_cached cached;
ge_p3_to_cached(&cached, &data[index1].point);
ge_p1p1 p1;
ge_add(&p1, &data[index2].point, &cached);
ge_p1p1_to_p3(&data[index2].point, &p1);
MULTIEXP_PERF(PERF_TIMER_PAUSE(add));
MULTIEXP_PERF(PERF_TIMER_RESUME(sub));
sc_sub(data[index1].scalar.bytes, data[index1].scalar.bytes, data[index2].scalar.bytes);
MULTIEXP_PERF(PERF_TIMER_PAUSE(sub));
MULTIEXP_PERF(PERF_TIMER_RESUME(push));
if (!(data[index1].scalar == rct::zero()))
{
heap.push_back(index1);
std::push_heap(heap.begin(), heap.end(), Comp);
}
heap.push_back(index2);
std::push_heap(heap.begin(), heap.end(), Comp);
MULTIEXP_PERF(PERF_TIMER_PAUSE(push));
}
MULTIEXP_PERF(PERF_TIMER_STOP(push));
MULTIEXP_PERF(PERF_TIMER_STOP(sub));
MULTIEXP_PERF(PERF_TIMER_STOP(add));
MULTIEXP_PERF(PERF_TIMER_STOP(pop));
MULTIEXP_PERF(PERF_TIMER_STOP(loop));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(end, 1000000));
//return rct::scalarmultKey(data[index1].point, data[index1].scalar);
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index1 = heap.back();
heap.pop_back();
ge_p2 p2;
ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point);
rct::key res;
ge_tobytes(res.bytes, &p2);
return res;
}
rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data)
{
MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
size_t points = data.size();
CHECK_AND_ASSERT_THROW_MES(points > 0, "Not enough points");
std::vector<size_t> heap;
heap.reserve(points);
for (size_t n = 0; n < points; ++n)
{
if (!(data[n].scalar == rct::zero()) && !ge_p3_is_point_at_infinity(&data[n].point))
heap.push_back(n);
}
points = heap.size();
if (points == 0)
return rct::identity();
auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; };
std::make_heap(heap.begin(), heap.end(), Comp);
if (points < 2)
{
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index1 = heap.back();
ge_p2 p2;
ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point);
rct::key res;
ge_tobytes(res.bytes, &p2);
return res;
}
MULTIEXP_PERF(PERF_TIMER_STOP(setup));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(loop, 1000000));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(pop, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(pop));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(div, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(div));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(add, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(add));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(sub, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(sub));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(push, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(push));
while (heap.size() > 1)
{
MULTIEXP_PERF(PERF_TIMER_RESUME(pop));
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index1 = heap.back();
heap.pop_back();
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index2 = heap.back();
heap.pop_back();
MULTIEXP_PERF(PERF_TIMER_PAUSE(pop));
ge_cached cached;
ge_p1p1 p1;
ge_p2 p2;
MULTIEXP_PERF(PERF_TIMER_RESUME(div));
while (1)
{
rct::key s1_2 = div2(data[index1].scalar);
if (!(data[index2].scalar < s1_2))
break;
if (data[index1].scalar.bytes[0] & 1)
{
data.resize(data.size()+1);
data.back().scalar = rct::identity();
data.back().point = data[index1].point;
heap.push_back(data.size() - 1);
std::push_heap(heap.begin(), heap.end(), Comp);
}
data[index1].scalar = div2(data[index1].scalar);
ge_p3_to_p2(&p2, &data[index1].point);
ge_p2_dbl(&p1, &p2);
ge_p1p1_to_p3(&data[index1].point, &p1);
}
MULTIEXP_PERF(PERF_TIMER_PAUSE(div));
MULTIEXP_PERF(PERF_TIMER_RESUME(add));
ge_p3_to_cached(&cached, &data[index1].point);
ge_add(&p1, &data[index2].point, &cached);
ge_p1p1_to_p3(&data[index2].point, &p1);
MULTIEXP_PERF(PERF_TIMER_PAUSE(add));
MULTIEXP_PERF(PERF_TIMER_RESUME(sub));
sc_sub(data[index1].scalar.bytes, data[index1].scalar.bytes, data[index2].scalar.bytes);
MULTIEXP_PERF(PERF_TIMER_PAUSE(sub));
MULTIEXP_PERF(PERF_TIMER_RESUME(push));
if (!(data[index1].scalar == rct::zero()))
{
heap.push_back(index1);
std::push_heap(heap.begin(), heap.end(), Comp);
}
heap.push_back(index2);
std::push_heap(heap.begin(), heap.end(), Comp);
MULTIEXP_PERF(PERF_TIMER_PAUSE(push));
}
MULTIEXP_PERF(PERF_TIMER_STOP(push));
MULTIEXP_PERF(PERF_TIMER_STOP(sub));
MULTIEXP_PERF(PERF_TIMER_STOP(add));
MULTIEXP_PERF(PERF_TIMER_STOP(pop));
MULTIEXP_PERF(PERF_TIMER_STOP(loop));
MULTIEXP_PERF(PERF_TIMER_START_UNIT(end, 1000000));
//return rct::scalarmultKey(data[index1].point, data[index1].scalar);
std::pop_heap(heap.begin(), heap.end(), Comp);
size_t index1 = heap.back();
heap.pop_back();
ge_p2 p2;
ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point);
rct::key res;
ge_tobytes(res.bytes, &p2);
return res;
}
static constexpr unsigned int STRAUS_C = 4;
struct straus_cached_data
{
#ifdef RAW_MEMORY_BLOCK
size_t size;
ge_cached *multiples;
straus_cached_data(): size(0), multiples(NULL) {}
~straus_cached_data() { aligned_free(multiples); }
#else
std::vector<std::vector<ge_cached>> multiples;
#endif
};
#ifdef RAW_MEMORY_BLOCK
#ifdef ALTERNATE_LAYOUT
#define CACHE_OFFSET(cache,point,digit) cache->multiples[(point)*((1<<STRAUS_C)-1)+((digit)-1)]
#else
#define CACHE_OFFSET(cache,point,digit) cache->multiples[(point)+cache->size*((digit)-1)]
#endif
#else
#ifdef ALTERNATE_LAYOUT
#define CACHE_OFFSET(cache,point,digit) local_cache->multiples[j][digit-1]
#else
#define CACHE_OFFSET(cache,point,digit) local_cache->multiples[digit][j]
#endif
#endif
std::shared_ptr<straus_cached_data> straus_init_cache(const std::vector<MultiexpData> &data, size_t N)
{
MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000));
if (N == 0)
N = data.size();
CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data");
ge_cached cached;
ge_p1p1 p1;
ge_p3 p3;
std::shared_ptr<straus_cached_data> cache(new straus_cached_data());
#ifdef RAW_MEMORY_BLOCK
const size_t offset = cache->size;
cache->multiples = (ge_cached*)aligned_realloc(cache->multiples, sizeof(ge_cached) * ((1<<STRAUS_C)-1) * std::max(offset, N), 4096);
CHECK_AND_ASSERT_THROW_MES(cache->multiples, "Out of memory");
cache->size = N;
for (size_t j=offset;j<N;++j)
{
ge_p3_to_cached(&CACHE_OFFSET(cache, j, 1), &data[j].point);
for (size_t i=2;i<1<<STRAUS_C;++i)
{
ge_add(&p1, &data[j].point, &CACHE_OFFSET(cache, j, i-1));
ge_p1p1_to_p3(&p3, &p1);
ge_p3_to_cached(&CACHE_OFFSET(cache, j, i), &p3);
}
}
#else
#ifdef ALTERNATE_LAYOUT
const size_t offset = cache->multiples.size();
cache->multiples.resize(std::max(offset, N));
for (size_t i = offset; i < N; ++i)
{
cache->multiples[i].resize((1<<STRAUS_C)-1);
ge_p3_to_cached(&cache->multiples[i][0], &data[i].point);
for (size_t j=2;j<1<<STRAUS_C;++j)
{
ge_add(&p1, &data[i].point, &cache->multiples[i][j-2]);
ge_p1p1_to_p3(&p3, &p1);
ge_p3_to_cached(&cache->multiples[i][j-1], &p3);
}
}
#else
cache->multiples.resize(1<<STRAUS_C);
size_t offset = cache->multiples[1].size();
cache->multiples[1].resize(std::max(offset, N));
for (size_t i = offset; i < N; ++i)
ge_p3_to_cached(&cache->multiples[1][i], &data[i].point);
for (size_t i=2;i<1<<STRAUS_C;++i)
cache->multiples[i].resize(std::max(offset, N));
for (size_t j=offset;j<N;++j)
{
for (size_t i=2;i<1<<STRAUS_C;++i)
{
ge_add(&p1, &data[j].point, &cache->multiples[i-1][j]);
ge_p1p1_to_p3(&p3, &p1);
ge_p3_to_cached(&cache->multiples[i][j], &p3);
}
}
#endif
#endif
MULTIEXP_PERF(PERF_TIMER_STOP(multiples));
return cache;
}
size_t straus_get_cache_size(const std::shared_ptr<straus_cached_data> &cache)
{
size_t sz = 0;
#ifdef RAW_MEMORY_BLOCK
sz += cache->size * sizeof(ge_cached) * ((1<<STRAUS_C)-1);
#else
for (const auto &e0: cache->multiples)
sz += e0.size() * sizeof(ge_cached);
#endif
return sz;
}
rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<straus_cached_data> &cache, size_t STEP)
{
CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small");
MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000));
bool HiGi = cache != NULL;
STEP = STEP ? STEP : 192;
MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
static constexpr unsigned int mask = (1<<STRAUS_C)-1;
std::shared_ptr<straus_cached_data> local_cache = cache == NULL ? straus_init_cache(data) : cache;
ge_cached cached;
ge_p1p1 p1;
ge_p3 p3;
#ifdef TRACK_STRAUS_ZERO_IDENTITY
MULTIEXP_PERF(PERF_TIMER_START_UNIT(skip, 1000000));
std::vector<uint8_t> skip(data.size());
for (size_t i = 0; i < data.size(); ++i)
skip[i] = data[i].scalar == rct::zero() || ge_p3_is_point_at_infinity(&data[i].point);
MULTIEXP_PERF(PERF_TIMER_STOP(skip));
#endif
MULTIEXP_PERF(PERF_TIMER_START_UNIT(digits, 1000000));
std::unique_ptr<uint8_t[]> digits{new uint8_t[256 * data.size()]};
for (size_t j = 0; j < data.size(); ++j)
{
unsigned char bytes33[33];
memcpy(bytes33, data[j].scalar.bytes, 32);
bytes33[32] = 0;
const unsigned char *bytes = bytes33;
#if 1
static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4");
unsigned int i;
for (i = 0; i < 256; i += 8, bytes++)
{
digits[j*256+i] = bytes[0] & 0xf;
digits[j*256+i+1] = (bytes[0] >> 1) & 0xf;
digits[j*256+i+2] = (bytes[0] >> 2) & 0xf;
digits[j*256+i+3] = (bytes[0] >> 3) & 0xf;
digits[j*256+i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf;
digits[j*256+i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf;
digits[j*256+i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf;
digits[j*256+i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf;
}
#elif 1
for (size_t i = 0; i < 256; ++i)
digits[j*256+i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask;
#else
rct::key shifted = data[j].scalar;
for (size_t i = 0; i < 256; ++i)
{
digits[j*256+i] = shifted.bytes[0] & 0xf;
shifted = div2(shifted, (256-i)>>3);
}
#endif
}
MULTIEXP_PERF(PERF_TIMER_STOP(digits));
rct::key maxscalar = rct::zero();
for (size_t i = 0; i < data.size(); ++i)
if (maxscalar < data[i].scalar)
maxscalar = data[i].scalar;
size_t start_i = 0;
while (start_i < 256 && !(maxscalar < pow2(start_i)))
start_i += STRAUS_C;
MULTIEXP_PERF(PERF_TIMER_STOP(setup));
ge_p3 res_p3 = ge_p3_identity;
for (size_t start_offset = 0; start_offset < data.size(); start_offset += STEP)
{
const size_t num_points = std::min(data.size() - start_offset, STEP);
ge_p3 band_p3 = ge_p3_identity;
size_t i = start_i;
if (!(i < STRAUS_C))
goto skipfirst;
while (!(i < STRAUS_C))
{
ge_p2 p2;
ge_p3_to_p2(&p2, &band_p3);
for (size_t j = 0; j < STRAUS_C; ++j)
{
ge_p2_dbl(&p1, &p2);
if (j == STRAUS_C - 1)
ge_p1p1_to_p3(&band_p3, &p1);
else
ge_p1p1_to_p2(&p2, &p1);
}
skipfirst:
i -= STRAUS_C;
for (size_t j = start_offset; j < start_offset + num_points; ++j)
{
#ifdef TRACK_STRAUS_ZERO_IDENTITY
if (skip[j])
continue;
#endif
const uint8_t digit = digits[j*256+i];
if (digit)
{
ge_add(&p1, &band_p3, &CACHE_OFFSET(local_cache, j, digit));
ge_p1p1_to_p3(&band_p3, &p1);
}
}
}
ge_p3_to_cached(&cached, &band_p3);
ge_add(&p1, &res_p3, &cached);
ge_p1p1_to_p3(&res_p3, &p1);
}
rct::key res;
ge_p3_tobytes(res.bytes, &res_p3);
return res;
}
size_t get_pippenger_c(size_t N)
{
// uncached: 2:1, 4:2, 8:2, 16:3, 32:4, 64:4, 128:5, 256:6, 512:7, 1024:7, 2048:8, 4096:9
// cached: 2:1, 4:2, 8:2, 16:3, 32:4, 64:4, 128:5, 256:6, 512:7, 1024:7, 2048:8, 4096:9
if (N <= 2) return 1;
if (N <= 8) return 2;
if (N <= 16) return 3;
if (N <= 64) return 4;
if (N <= 128) return 5;
if (N <= 256) return 6;
if (N <= 1024) return 7;
if (N <= 2048) return 8;
return 9;
}
struct pippenger_cached_data
{
size_t size;
ge_cached *cached;
pippenger_cached_data(): size(0), cached(NULL) {}
~pippenger_cached_data() { aligned_free(cached); }
};
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t N)
{
MULTIEXP_PERF(PERF_TIMER_START_UNIT(pippenger_init_cache, 1000000));
if (N == 0)
N = data.size();
CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data");
ge_cached cached;
std::shared_ptr<pippenger_cached_data> cache(new pippenger_cached_data());
cache->size = N;
cache->cached = (ge_cached*)aligned_realloc(cache->cached, N * sizeof(ge_cached), 4096);
CHECK_AND_ASSERT_THROW_MES(cache->cached, "Out of memory");
for (size_t i = 0; i < N; ++i)
ge_p3_to_cached(&cache->cached[i], &data[i].point);
MULTIEXP_PERF(PERF_TIMER_STOP(pippenger_init_cache));
return cache;
}
size_t pippenger_get_cache_size(const std::shared_ptr<pippenger_cached_data> &cache)
{
return cache->size * sizeof(*cache->cached);
}
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache, size_t c)
{
CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small");
if (c == 0)
c = get_pippenger_c(data.size());
CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large");
ge_p3 result = ge_p3_identity;
std::unique_ptr<ge_p3[]> buckets{new ge_p3[1<<c]};
std::shared_ptr<pippenger_cached_data> local_cache = cache == NULL ? pippenger_init_cache(data) : cache;
rct::key maxscalar = rct::zero();
for (size_t i = 0; i < data.size(); ++i)
{
if (maxscalar < data[i].scalar)
maxscalar = data[i].scalar;
}
size_t groups = 0;
while (groups < 256 && !(maxscalar < pow2(groups)))
++groups;
groups = (groups + c - 1) / c;
for (size_t k = groups; k-- > 0; )
{
if (!ge_p3_is_point_at_infinity(&result))
{
ge_p2 p2;
ge_p3_to_p2(&p2, &result);
for (size_t i = 0; i < c; ++i)
{
ge_p1p1 p1;
ge_p2_dbl(&p1, &p2);
if (i == c - 1)
ge_p1p1_to_p3(&result, &p1);
else
ge_p1p1_to_p2(&p2, &p1);
}
}
for (size_t i = 0; i < (1u<<c); ++i)
buckets[i] = ge_p3_identity;
// partition scalars into buckets
for (size_t i = 0; i < data.size(); ++i)
{
unsigned int bucket = 0;
for (size_t j = 0; j < c; ++j)
if (test(data[i].scalar, k*c+j))
bucket |= 1<<j;
if (bucket == 0)
continue;
CHECK_AND_ASSERT_THROW_MES(bucket < (1u<<c), "bucket overflow");
if (!ge_p3_is_point_at_infinity(&buckets[bucket]))
{
add(buckets[bucket], local_cache->cached[i]);
}
else
buckets[bucket] = data[i].point;
}
// sum the buckets
ge_p3 pail = ge_p3_identity;
for (size_t i = (1<<c)-1; i > 0; --i)
{
if (!ge_p3_is_point_at_infinity(&buckets[i]))
add(pail, buckets[i]);
if (!ge_p3_is_point_at_infinity(&pail))
add(result, pail);
}
}
rct::key res;
ge_p3_tobytes(res.bytes, &result);
return res;
}
}

71
src/ringct/multiexp.h Normal file
View File

@ -0,0 +1,71 @@
// Copyright (c) 2017, 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.
//
// Adapted from Python code by Sarang Noether
#pragma once
#ifndef MULTIEXP_H
#define MULTIEXP_H
#include <vector>
#include "crypto/crypto.h"
#include "rctTypes.h"
#include "misc_log_ex.h"
namespace rct
{
struct MultiexpData {
rct::key scalar;
ge_p3 point;
MultiexpData() {}
MultiexpData(const rct::key &s, const ge_p3 &p): scalar(s), point(p) {}
MultiexpData(const rct::key &s, const rct::key &p): scalar(s)
{
CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&point, p.bytes) == 0, "ge_frombytes_vartime failed");
}
};
struct straus_cached_data;
struct pippenger_cached_data;
rct::key bos_coster_heap_conv(std::vector<MultiexpData> data);
rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data);
std::shared_ptr<straus_cached_data> straus_init_cache(const std::vector<MultiexpData> &data, size_t N =0);
size_t straus_get_cache_size(const std::shared_ptr<straus_cached_data> &cache);
rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<straus_cached_data> &cache = NULL, size_t STEP = 0);
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t N =0);
size_t pippenger_get_cache_size(const std::shared_ptr<pippenger_cached_data> &cache);
size_t get_pippenger_c(size_t N);
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache = NULL, size_t c = 0);
}
#endif

View File

@ -60,6 +60,17 @@ namespace rct {
//Various key generation functions //Various key generation functions
bool toPointCheckOrder(ge_p3 *P, const unsigned char *data)
{
if (ge_frombytes_vartime(P, data))
return false;
ge_p2 R;
ge_scalarmult(&R, curveOrder().bytes, P);
key tmp;
ge_tobytes(tmp.bytes, &R);
return tmp == identity();
}
//generates a random scalar which can be used as a secret key or mask //generates a random scalar which can be used as a secret key or mask
void skGen(key &sk) { void skGen(key &sk) {
random32_unbiased(sk.bytes); random32_unbiased(sk.bytes);
@ -193,15 +204,33 @@ namespace rct {
//Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint
key scalarmultH(const key & a) { key scalarmultH(const key & a) {
ge_p3 A;
ge_p2 R; ge_p2 R;
CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &ge_p3_H);
ge_scalarmult(&R, a.bytes, &A);
key aP; key aP;
ge_tobytes(aP.bytes, &R); ge_tobytes(aP.bytes, &R);
return aP; return aP;
} }
//Computes 8P
key scalarmult8(const key & P) {
ge_p3 p3;
CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&p3, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_p2 p2;
ge_p3_to_p2(&p2, &p3);
ge_p1p1 p1;
ge_mul8(&p1, &p2);
ge_p1p1_to_p2(&p2, &p1);
rct::key res;
ge_tobytes(res.bytes, &p2);
return res;
}
//Computes aL where L is the curve order
bool isInMainSubgroup(const key & a) {
ge_p3 p3;
return toPointCheckOrder(&p3, a.bytes);
}
//Curve addition / subtractions //Curve addition / subtractions
//for curve points: AB = A + B //for curve points: AB = A + B

View File

@ -63,6 +63,8 @@ namespace rct {
static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } };
static const key G = { {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 } }; static const key G = { {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 } };
static const key EIGHT = { {0x08, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const key INV_EIGHT = { { 0x79, 0x2f, 0xdc, 0xe2, 0x29, 0xe5, 0x06, 0x61, 0xd0, 0xda, 0x1c, 0x7d, 0xb3, 0x9d, 0xd3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 } };
//Creates a zero scalar //Creates a zero scalar
inline key zero() { return Z; } inline key zero() { return Z; }
@ -83,6 +85,7 @@ namespace rct {
keyM keyMInit(size_t rows, size_t cols); keyM keyMInit(size_t rows, size_t cols);
//Various key generation functions //Various key generation functions
bool toPointCheckOrder(ge_p3 *P, const unsigned char *data);
//generates a random scalar which can be used as a secret key or mask //generates a random scalar which can be used as a secret key or mask
key skGen(); key skGen();
@ -119,6 +122,10 @@ namespace rct {
key scalarmultKey(const key &P, const key &a); key scalarmultKey(const key &P, const key &a);
//Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint
key scalarmultH(const key & a); key scalarmultH(const key & a);
// multiplies a point by 8
key scalarmult8(const key & P);
// checks a is in the main subgroup (ie, not a small one)
bool isInMainSubgroup(const key & a);
//Curve addition / subtractions //Curve addition / subtractions

View File

@ -45,30 +45,6 @@ using namespace std;
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}} #define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
namespace rct { namespace rct {
bool is_simple(int type)
{
switch (type)
{
case RCTTypeSimple:
case RCTTypeSimpleBulletproof:
return true;
default:
return false;
}
}
bool is_bulletproof(int type)
{
switch (type)
{
case RCTTypeSimpleBulletproof:
case RCTTypeFullBulletproof:
return true;
default:
return false;
}
}
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
{ {
mask = rct::skGen(); mask = rct::skGen();
@ -78,6 +54,15 @@ namespace rct {
return proof; return proof;
} }
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
{
masks = rct::skvGen(amounts.size());
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;
return proof;
}
bool verBulletproof(const Bulletproof &proof) bool verBulletproof(const Bulletproof &proof)
{ {
try { return bulletproof_VERIFY(proof); } try { return bulletproof_VERIFY(proof); }
@ -85,6 +70,13 @@ namespace rct {
catch (...) { return false; } catch (...) { return false; }
} }
bool verBulletproof(const std::vector<const Bulletproof*> &proofs)
{
try { return bulletproof_VERIFY(proofs); }
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (...) { return false; }
}
//Borromean (c.f. gmax/andytoshi's paper) //Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key64 L[2], alpha; key64 L[2], alpha;
@ -285,6 +277,7 @@ namespace rct {
for (j = 0; j < dsRows; j++) { for (j = 0; j < dsRows; j++) {
addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
hashToPoint(Hi, pk[i][j]); hashToPoint(Hi, pk[i][j]);
CHECK_AND_ASSERT_MES(!(Hi == rct::identity()), false, "Data hashed to point at infinity");
addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
toHash[3 * j + 1] = pk[i][j]; toHash[3 * j + 1] = pk[i][j];
toHash[3 * j + 2] = L; toHash[3 * j + 2] = L;
@ -389,7 +382,7 @@ namespace rct {
std::stringstream ss; std::stringstream ss;
binary_archive<true> ba(ss); binary_archive<true> ba(ss);
CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing"); CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing");
const size_t inputs = is_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size(); const size_t inputs = is_rct_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size();
const size_t outputs = rv.ecdhInfo.size(); const size_t outputs = rv.ecdhInfo.size();
key prehash; key prehash;
CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs), CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs),
@ -398,7 +391,7 @@ namespace rct {
hashes.push_back(hash2rct(h)); hashes.push_back(hash2rct(h));
keyV kv; keyV kv;
if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof) if (rv.type == RCTTypeBulletproof)
{ {
kv.reserve((6*2+9) * rv.p.bulletproofs.size()); kv.reserve((6*2+9) * rv.p.bulletproofs.size());
for (const auto &p: rv.p.bulletproofs) for (const auto &p: rv.p.bulletproofs)
@ -659,7 +652,7 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number // 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 // 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 // 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, bool bulletproof, 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, 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(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(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
@ -669,12 +662,9 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present"); CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
rctSig rv; rctSig rv;
rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.type = RCTTypeFull;
rv.message = message; rv.message = message;
rv.outPk.resize(destinations.size()); rv.outPk.resize(destinations.size());
if (bulletproof)
rv.p.bulletproofs.resize(destinations.size());
else
rv.p.rangeSigs.resize(destinations.size()); rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size());
@ -685,17 +675,10 @@ namespace rct {
//add destination to sig //add destination to sig
rv.outPk[i].dest = copy(destinations[i]); rv.outPk[i].dest = copy(destinations[i]);
//compute range proof //compute range proof
if (bulletproof)
rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
else
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
#ifdef DBG #ifdef DBG
if (bulletproof)
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof");
else
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif #endif
//mask amount and mask //mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]); rv.ecdhInfo[i].amount = d2h(amounts[i]);
@ -725,12 +708,13 @@ namespace rct {
ctkeyM mixRing; ctkeyM mixRing;
ctkeyV outSk; ctkeyV outSk;
tie(mixRing, index) = populateFromBlockchain(inPk, mixin); tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev); return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, hwdev);
} }
//RCT simple //RCT simple
//for post-rct only //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, bool bulletproof, hw::device &hwdev) { 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;
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); 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(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
@ -746,35 +730,74 @@ namespace rct {
} }
rctSig rv; rctSig rv;
rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple; rv.type = bulletproof ? RCTTypeBulletproof : RCTTypeSimple;
rv.message = message; rv.message = message;
rv.outPk.resize(destinations.size()); rv.outPk.resize(destinations.size());
if (bulletproof) if (!bulletproof)
rv.p.bulletproofs.resize(destinations.size());
else
rv.p.rangeSigs.resize(destinations.size()); rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size());
size_t i; size_t i;
keyV masks(destinations.size()); //sk mask.. keyV masks(destinations.size()); //sk mask..
outSk.resize(destinations.size()); outSk.resize(destinations.size());
key sumout = zero();
for (i = 0; i < destinations.size(); i++) { for (i = 0; i < destinations.size(); i++) {
//add destination to sig //add destination to sig
rv.outPk[i].dest = copy(destinations[i]); rv.outPk[i].dest = copy(destinations[i]);
//compute range proof //compute range proof
if (bulletproof) if (!bulletproof)
rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
else
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
#ifdef DBG #ifdef DBG
if (bulletproof) if (!bulletproof)
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof");
else
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif #endif
}
rv.p.bulletproofs.clear();
if (bulletproof)
{
std::vector<uint64_t> proof_amounts;
size_t n_amounts = outamounts.size();
size_t amounts_proved = 0;
if (range_proof_type == RangeProofPaddedBulletproof)
{
rct::keyV C, masks;
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = rct::scalarmult8(C[i]);
outSk[i].mask = masks[i];
}
}
else while (amounts_proved < n_amounts)
{
size_t batch_size = 1;
if (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));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
outSk[i + amounts_proved].mask = masks[i];
}
amounts_proved += batch_size;
}
}
key sumout = zero();
for (i = 0; i < outSk.size(); ++i)
{
sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes);
//mask amount and mask //mask amount and mask
@ -822,7 +845,7 @@ namespace rct {
mixRing[i].resize(mixin+1); mixRing[i].resize(mixin+1);
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
} }
return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev); return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev);
} }
//RingCT protocol //RingCT protocol
@ -837,12 +860,9 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number // must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) { bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct); PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
if (semantics) if (semantics)
{ {
if (rv.type == RCTTypeFullBulletproof)
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
else
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG");
@ -860,19 +880,13 @@ namespace rct {
tools::threadpool::waiter waiter; tools::threadpool::waiter waiter;
std::deque<bool> results(rv.outPk.size(), false); std::deque<bool> results(rv.outPk.size(), false);
DP("range proofs verified?"); DP("range proofs verified?");
for (size_t i = 0; i < rv.outPk.size(); i++) { for (size_t i = 0; i < rv.outPk.size(); i++)
tpool.submit(&waiter, [&, i] { tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
if (rv.p.rangeSigs.empty())
results[i] = verBulletproof(rv.p.bulletproofs[i]);
else
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
}, true);
}
waiter.wait(&tpool); waiter.wait(&tpool);
for (size_t i = 0; i < rv.outPk.size(); ++i) { for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) { if (!results[i]) {
LOG_PRINT_L1("Range proof verified failed for output " << i); LOG_PRINT_L1("Range proof verified failed for proof " << i);
return false; return false;
} }
} }
@ -906,17 +920,26 @@ namespace rct {
//ver RingCT simple //ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity) //assumes only post-rct style inputs (at least for max anonymity)
bool verRctSimple(const rctSig & rv, bool semantics) { bool verRctSemanticsSimple(const std::vector<const rctSig*> & rvv) {
try try
{ {
PERF_TIMER(verRctSimple); PERF_TIMER(verRctSemanticsSimple);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig"); tools::threadpool& tpool = tools::threadpool::getInstance();
if (semantics) tools::threadpool::waiter waiter;
std::deque<bool> results;
std::vector<const Bulletproof*> proofs;
size_t max_non_bp_proofs = 0, offset = 0;
for (const rctSig *rvp: rvv)
{ {
if (rv.type == RCTTypeSimpleBulletproof) 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");
const bool bulletproof = is_rct_bulletproof(rv.type);
if (bulletproof)
{ {
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty");
} }
@ -927,25 +950,19 @@ namespace rct {
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty");
} }
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
if (!bulletproof)
max_non_bp_proofs += rv.p.rangeSigs.size();
} }
else
results.resize(max_non_bp_proofs);
for (const rctSig *rvp: rvv)
{ {
// semantics check is early, and mixRing/MGs aren't resolved yet const rctSig &rv = *rvp;
if (rv.type == RCTTypeSimpleBulletproof)
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
else
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
}
const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); const bool bulletproof = is_rct_bulletproof(rv.type);
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
std::deque<bool> results(threads);
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
const keyV &pseudoOuts = is_bulletproof(rv.type) ? rv.p.pseudoOuts : rv.pseudoOuts;
if (semantics) {
key sumOutpks = identity(); key sumOutpks = identity();
for (size_t i = 0; i < rv.outPk.size(); i++) { for (size_t i = 0; i < rv.outPk.size(); i++) {
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
@ -966,26 +983,75 @@ namespace rct {
return false; return false;
} }
results.clear(); if (bulletproof)
results.resize(rv.outPk.size()); {
for (size_t i = 0; i < rv.outPk.size(); i++) { for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
tpool.submit(&waiter, [&, i] { proofs.push_back(&rv.p.bulletproofs[i]);
if (rv.p.rangeSigs.empty()) }
results[i] = verBulletproof(rv.p.bulletproofs[i]); else
else {
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); for (size_t i = 0; i < rv.p.rangeSigs.size(); i++)
}, true); tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
offset += rv.p.rangeSigs.size();
}
}
if (!proofs.empty() && !verBulletproof(proofs))
{
LOG_PRINT_L1("Aggregate range proof verified failed");
return false;
} }
waiter.wait(&tpool);
waiter.wait(&tpool);
for (size_t i = 0; i < results.size(); ++i) { for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) { if (!results[i]) {
LOG_PRINT_L1("Range proof verified failed for output " << i); LOG_PRINT_L1("Range proof verified failed for proof " << i);
return false; return false;
} }
} }
return true;
} }
else { // we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (const std::exception &e)
{
LOG_PRINT_L1("Error in verRctSemanticsSimple: " << e.what());
return false;
}
catch (...)
{
LOG_PRINT_L1("Error in verRctSemanticsSimple, but not an actual exception");
return false;
}
}
bool verRctSemanticsSimple(const rctSig & rv)
{
return verRctSemanticsSimple(std::vector<const rctSig*>(1, &rv));
}
//ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity)
bool verRctNonSemanticsSimple(const rctSig & rv) {
try
{
PERF_TIMER(verRctNonSemanticsSimple);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, 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)
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
else
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
std::deque<bool> results(threads);
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
const key message = get_pre_mlsag_hash(rv, hw::get_device("default")); const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
results.clear(); results.clear();
@ -993,7 +1059,7 @@ namespace rct {
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
tpool.submit(&waiter, [&, i] { tpool.submit(&waiter, [&, i] {
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
}, true); });
} }
waiter.wait(&tpool); waiter.wait(&tpool);
@ -1003,19 +1069,18 @@ namespace rct {
return false; return false;
} }
} }
}
return true; return true;
} }
// we can get deep throws from ge_frombytes_vartime if input isn't valid // we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (const std::exception &e) catch (const std::exception &e)
{ {
LOG_PRINT_L1("Error in verRct: " << e.what()); LOG_PRINT_L1("Error in verRctNonSemanticsSimple: " << e.what());
return false; return false;
} }
catch (...) catch (...)
{ {
LOG_PRINT_L1("Error in verRct, but not an actual exception"); LOG_PRINT_L1("Error in verRctNonSemanticsSimple, but not an actual exception");
return false; return false;
} }
} }
@ -1031,7 +1096,7 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment // 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 // must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) { xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); 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"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
@ -1044,6 +1109,8 @@ namespace rct {
DP("C"); DP("C");
DP(C); DP(C);
key Ctmp; key Ctmp;
CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask");
CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount");
addKeys2(Ctmp, mask, amount, H); addKeys2(Ctmp, mask, amount, H);
DP("Ctmp"); DP("Ctmp");
DP(Ctmp); DP(Ctmp);
@ -1059,7 +1126,7 @@ namespace rct {
} }
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) { 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 == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); 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"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
@ -1072,6 +1139,8 @@ namespace rct {
DP("C"); DP("C");
DP(C); DP(C);
key Ctmp; key Ctmp;
CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask");
CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount");
addKeys2(Ctmp, mask, amount, H); addKeys2(Ctmp, mask, amount, H);
DP("Ctmp"); DP("Ctmp");
DP(Ctmp); DP(Ctmp);
@ -1087,12 +1156,12 @@ namespace rct {
} }
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { 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 == RCTTypeFullBulletproof || rv.type == RCTTypeSimpleBulletproof, CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof,
false, "unsupported rct type"); false, "unsupported rct type");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); 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"); CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
if (rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof) if (rv.type == RCTTypeFull)
{ {
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
} }

View File

@ -119,14 +119,16 @@ namespace rct {
//decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) //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 // 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 // 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, bool bulletproof, 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, 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 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 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, bool bulletproof, 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);
bool verRct(const rctSig & rv, bool semantics); bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSimple(const rctSig & rv, bool semantics); bool verRctSemanticsSimple(const rctSig & rv);
static inline bool verRctSimple(const rctSig & rv) { return verRctSimple(rv, true) && verRctSimple(rv, false); } bool verRctSemanticsSimple(const std::vector<const rctSig*> & rv);
bool verRctNonSemanticsSimple(const rctSig & rv);
static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); }
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);

View File

@ -28,6 +28,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // 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. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "misc_log_ex.h"
#include "cryptonote_config.h"
#include "rctTypes.h" #include "rctTypes.h"
using namespace crypto; using namespace crypto;
using namespace std; using namespace std;
@ -209,4 +211,90 @@ namespace rct {
return vali; return vali;
} }
bool is_rct_simple(int type)
{
switch (type)
{
case RCTTypeSimple:
case RCTTypeBulletproof:
return true;
default:
return false;
}
}
bool is_rct_bulletproof(int type)
{
switch (type)
{
case RCTTypeBulletproof:
return true;
default:
return false;
}
}
bool is_rct_borromean(int type)
{
switch (type)
{
case RCTTypeSimple:
case RCTTypeFull:
return true;
default:
return false;
}
}
size_t n_bulletproof_amounts(const Bulletproof &proof)
{
CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4;
static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof");
return proof.V.size();
}
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
for (const Bulletproof &proof: proofs)
{
size_t n2 = n_bulletproof_amounts(proof);
CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
if (n2 == 0)
return 0;
n += n2;
}
return n;
}
size_t n_bulletproof_max_amounts(const Bulletproof &proof)
{
CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4;
static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
return 1 << (proof.L.size() - 6);
}
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
for (const Bulletproof &proof: proofs)
{
size_t n2 = n_bulletproof_max_amounts(proof);
CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
if (n2 == 0)
return 0;
n += n2;
}
return n;
}
} }

View File

@ -190,6 +190,8 @@ namespace rct {
Bulletproof() {} Bulletproof() {}
Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
BEGIN_SERIALIZE_OBJECT() BEGIN_SERIALIZE_OBJECT()
// Commitments aren't saved, they're restored via outPk // Commitments aren't saved, they're restored via outPk
@ -211,6 +213,11 @@ namespace rct {
END_SERIALIZE() END_SERIALIZE()
}; };
size_t n_bulletproof_amounts(const Bulletproof &proof);
size_t n_bulletproof_max_amounts(const Bulletproof &proof);
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs);
//A container to hold all signatures necessary for RingCT //A container to hold all signatures necessary for RingCT
// rangeSigs holds all the rangeproof data of a transaction // rangeSigs holds all the rangeproof data of a transaction
// MG holds the MLSAG signature of a transaction // MG holds the MLSAG signature of a transaction
@ -222,9 +229,9 @@ namespace rct {
RCTTypeNull = 0, RCTTypeNull = 0,
RCTTypeFull = 1, RCTTypeFull = 1,
RCTTypeSimple = 2, RCTTypeSimple = 2,
RCTTypeFullBulletproof = 3, RCTTypeBulletproof = 3,
RCTTypeSimpleBulletproof = 4,
}; };
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct rctSigBase { struct rctSigBase {
uint8_t type; uint8_t type;
key message; key message;
@ -241,7 +248,7 @@ namespace rct {
FIELD(type) FIELD(type)
if (type == RCTTypeNull) if (type == RCTTypeNull)
return true; return true;
if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof)
return false; return false;
VARINT_FIELD(txnFee) VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help // inputs/outputs not saved, only here for serialization help
@ -302,21 +309,25 @@ namespace rct {
{ {
if (type == RCTTypeNull) if (type == RCTTypeNull)
return true; return true;
if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof)
return false; return false;
if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof) if (type == RCTTypeBulletproof)
{ {
ar.tag("bp"); ar.tag("bp");
ar.begin_array(); ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); uint32_t nbp = bulletproofs.size();
if (bulletproofs.size() != outputs) FIELD(nbp)
if (nbp > outputs)
return false; return false;
for (size_t i = 0; i < outputs; ++i) PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs);
for (size_t i = 0; i < nbp; ++i)
{ {
FIELDS(bulletproofs[i]) FIELDS(bulletproofs[i])
if (outputs - i > 1) if (nbp - i > 1)
ar.delimit_array(); ar.delimit_array();
} }
if (n_bulletproof_max_amounts(bulletproofs) < outputs)
return false;
ar.end_array(); ar.end_array();
} }
else else
@ -339,7 +350,7 @@ namespace rct {
ar.begin_array(); ar.begin_array();
// we keep a byte for size of MGs, because we don't know whether this is // 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 // a simple or full rct signature, and it's starting to annoy the hell out of me
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1; size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof) ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
if (MGs.size() != mg_elements) if (MGs.size() != mg_elements)
return false; return false;
@ -357,7 +368,7 @@ namespace rct {
for (size_t j = 0; j < mixin + 1; ++j) for (size_t j = 0; j < mixin + 1; ++j)
{ {
ar.begin_array(); ar.begin_array();
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1; size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof) ? 1 : inputs) + 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
if (MGs[i].ss[j].size() != mg_ss2_elements) if (MGs[i].ss[j].size() != mg_ss2_elements)
return false; return false;
@ -383,7 +394,7 @@ namespace rct {
ar.delimit_array(); ar.delimit_array();
} }
ar.end_array(); ar.end_array();
if (type == RCTTypeSimpleBulletproof) if (type == RCTTypeBulletproof)
{ {
ar.tag("pseudoOuts"); ar.tag("pseudoOuts");
ar.begin_array(); ar.begin_array();
@ -407,12 +418,12 @@ namespace rct {
keyV& get_pseudo_outs() keyV& get_pseudo_outs()
{ {
return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts;
} }
keyV const& get_pseudo_outs() const keyV const& get_pseudo_outs() const
{ {
return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts;
} }
}; };
@ -517,6 +528,10 @@ namespace rct {
//int[64] to uint long long //int[64] to uint long long
xmr_amount b2d(bits amountb); xmr_amount b2d(bits amountb);
bool is_rct_simple(int type);
bool is_rct_bulletproof(int type);
bool is_rct_borromean(int type);
static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }
static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; }

View File

@ -195,8 +195,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET; res.stagenet = m_nettype == STAGENET;
res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time(); res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
@ -1313,7 +1313,7 @@ namespace cryptonote
response.hash = string_tools::pod_to_hex(hash); response.hash = string_tools::pod_to_hex(hash);
response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); response.difficulty = m_core.get_blockchain_storage().block_difficulty(height);
response.reward = get_block_reward(blk); response.reward = get_block_reward(blk);
response.block_size = m_core.get_blockchain_storage().get_db().get_block_size(height); 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.num_txes = blk.tx_hashes.size();
response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : "";
return true; return true;
@ -1646,8 +1646,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET; res.stagenet = m_nettype == STAGENET;
res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time(); res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
@ -1839,14 +1839,15 @@ namespace cryptonote
return true; return true;
} }
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp)
{ {
PERF_TIMER(on_get_per_kb_fee_estimate); PERF_TIMER(on_get_base_fee_estimate);
bool r; bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BASE_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r))
return r; return r;
res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
res.quantization_mask = Blockchain::get_fee_quantization_mask();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
return true; return true;
} }

View File

@ -150,7 +150,7 @@ namespace cryptonote
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted) MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted)
MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE)
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted) MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
@ -214,7 +214,7 @@ namespace cryptonote
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp); bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp); bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp);
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp); bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp);
bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp);
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp);
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp);
bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp); bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp);

View File

@ -48,8 +48,8 @@ namespace cryptonote
// whether they can talk to a given daemon without having to know in // whether they can talk to a given daemon without having to know in
// advance which version they will stop working with // advance which version they will stop working with
// Don't go over 32767 for any of these // Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1 #define CORE_RPC_VERSION_MAJOR 2
#define CORE_RPC_VERSION_MINOR 21 #define CORE_RPC_VERSION_MINOR 0
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@ -959,7 +959,9 @@ namespace cryptonote
std::string top_block_hash; std::string top_block_hash;
uint64_t cumulative_difficulty; uint64_t cumulative_difficulty;
uint64_t block_size_limit; uint64_t block_size_limit;
uint64_t block_weight_limit;
uint64_t block_size_median; uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t start_time; uint64_t start_time;
uint64_t free_space; uint64_t free_space;
bool offline; bool offline;
@ -990,7 +992,9 @@ namespace cryptonote
KV_SERIALIZE(top_block_hash) KV_SERIALIZE(top_block_hash)
KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(block_size_limit) KV_SERIALIZE(block_size_limit)
KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0)
KV_SERIALIZE(block_size_median) KV_SERIALIZE(block_size_median)
KV_SERIALIZE_OPT(block_weight_median, (uint64_t)0)
KV_SERIALIZE(start_time) KV_SERIALIZE(start_time)
KV_SERIALIZE(free_space) KV_SERIALIZE(free_space)
KV_SERIALIZE(offline) KV_SERIALIZE(offline)
@ -1195,6 +1199,7 @@ namespace cryptonote
difficulty_type difficulty; difficulty_type difficulty;
uint64_t reward; uint64_t reward;
uint64_t block_size; uint64_t block_size;
uint64_t block_weight;
uint64_t num_txes; uint64_t num_txes;
std::string pow_hash; std::string pow_hash;
@ -1211,6 +1216,7 @@ namespace cryptonote
KV_SERIALIZE(difficulty) KV_SERIALIZE(difficulty)
KV_SERIALIZE(reward) KV_SERIALIZE(reward)
KV_SERIALIZE(block_size) KV_SERIALIZE(block_size)
KV_SERIALIZE_OPT(block_weight, (uint64_t)0)
KV_SERIALIZE(num_txes) KV_SERIALIZE(num_txes)
KV_SERIALIZE(pow_hash) KV_SERIALIZE(pow_hash)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
@ -1451,6 +1457,7 @@ namespace cryptonote
std::string id_hash; std::string id_hash;
std::string tx_json; // TODO - expose this data directly std::string tx_json; // TODO - expose this data directly
uint64_t blob_size; uint64_t blob_size;
uint64_t weight;
uint64_t fee; uint64_t fee;
std::string max_used_block_id_hash; std::string max_used_block_id_hash;
uint64_t max_used_block_height; uint64_t max_used_block_height;
@ -1468,6 +1475,7 @@ namespace cryptonote
KV_SERIALIZE(id_hash) KV_SERIALIZE(id_hash)
KV_SERIALIZE(tx_json) KV_SERIALIZE(tx_json)
KV_SERIALIZE(blob_size) KV_SERIALIZE(blob_size)
KV_SERIALIZE_OPT(weight, (uint64_t)0)
KV_SERIALIZE(fee) KV_SERIALIZE(fee)
KV_SERIALIZE(max_used_block_id_hash) KV_SERIALIZE(max_used_block_id_hash)
KV_SERIALIZE(max_used_block_height) KV_SERIALIZE(max_used_block_height)
@ -1564,7 +1572,7 @@ namespace cryptonote
struct tx_backlog_entry struct tx_backlog_entry
{ {
uint64_t blob_size; uint64_t weight;
uint64_t fee; uint64_t fee;
uint64_t time_in_pool; uint64_t time_in_pool;
}; };
@ -2102,7 +2110,7 @@ namespace cryptonote
}; };
}; };
struct COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE struct COMMAND_RPC_GET_BASE_FEE_ESTIMATE
{ {
struct request struct request
{ {
@ -2117,11 +2125,13 @@ namespace cryptonote
{ {
std::string status; std::string status;
uint64_t fee; uint64_t fee;
uint64_t quantization_mask;
bool untrusted; bool untrusted;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status) KV_SERIALIZE(status)
KV_SERIALIZE(fee) KV_SERIALIZE(fee)
KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1)
KV_SERIALIZE(untrusted) KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };

View File

@ -472,7 +472,8 @@ namespace rpc
res.info.testnet = m_core.get_nettype() == TESTNET; res.info.testnet = m_core.get_nettype() == TESTNET;
res.info.stagenet = m_core.get_nettype() == STAGENET; res.info.stagenet = m_core.get_nettype() == STAGENET;
res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.start_time = (uint64_t)m_core.get_start_time(); res.info.start_time = (uint64_t)m_core.get_start_time();
res.status = Message::STATUS_OK; res.status = Message::STATUS_OK;
@ -763,7 +764,7 @@ namespace rpc
void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
{ {
res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks); res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
res.status = Message::STATUS_OK; res.status = Message::STATUS_OK;
} }

View File

@ -85,6 +85,7 @@ namespace rpc
cryptonote::transaction tx; cryptonote::transaction tx;
crypto::hash tx_hash; crypto::hash tx_hash;
uint64_t blob_size; uint64_t blob_size;
uint64_t weight;
uint64_t fee; uint64_t fee;
crypto::hash max_used_block_hash; crypto::hash max_used_block_hash;
uint64_t max_used_block_height; uint64_t max_used_block_height;
@ -185,6 +186,9 @@ namespace rpc
crypto::hash top_block_hash; crypto::hash top_block_hash;
uint64_t cumulative_difficulty; uint64_t cumulative_difficulty;
uint64_t block_size_limit; uint64_t block_size_limit;
uint64_t block_weight_limit;
uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t start_time; uint64_t start_time;
}; };

View File

@ -757,6 +757,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx
INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx); INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx);
INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash); INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash);
INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size); INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size);
INSERT_INTO_JSON_OBJECT(val, doc, weight, tx.weight);
INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee); INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee);
INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash);
INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height);
@ -780,6 +781,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
GET_FROM_JSON_OBJECT(val, tx.tx, tx); GET_FROM_JSON_OBJECT(val, tx.tx, tx);
GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size); GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size);
GET_FROM_JSON_OBJECT(val, tx.weight, weight);
GET_FROM_JSON_OBJECT(val, tx.fee, fee); GET_FROM_JSON_OBJECT(val, tx.fee, fee);
GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash); GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash);
GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height); GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height);
@ -1195,6 +1197,9 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in
INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit); INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_limit, info.block_weight_limit);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median);
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median);
INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time); INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
} }
@ -1221,6 +1226,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit);
GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median);
GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median);
GET_FROM_JSON_OBJECT(val, info.start_time, start_time); GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
} }

View File

@ -86,9 +86,9 @@ typedef cryptonote::simple_wallet sw;
#define EXTENDED_LOGS_FILE "wallet_details.log" #define EXTENDED_LOGS_FILE "wallet_details.log"
#define DEFAULT_MIX 6 #define DEFAULT_MIX 10
#define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol #define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol
#define LOCK_IDLE_SCOPE() \ #define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
@ -829,21 +829,24 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std:
fail_msg_writer() << tr("Cannot connect to daemon"); fail_msg_writer() << tr("Cannot connect to daemon");
return true; return true;
} }
const uint64_t per_kb_fee = m_wallet->get_per_kb_fee(); const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t typical_size_kb = 13; const uint64_t base_fee = m_wallet->get_base_fee();
message_writer() << (boost::format(tr("Current fee is %s %s per kB")) % print_money(per_kb_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str(); const char *base = per_byte ? "byte" : "kB";
const uint64_t typical_size = per_byte ? 2500 : 13;
const uint64_t size_granularity = per_byte ? 1 : 1024;
message_writer() << (boost::format(tr("Current fee is %s %s per %s")) % print_money(base_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % base).str();
std::vector<uint64_t> fees; std::vector<uint64_t> fees;
for (uint32_t priority = 1; priority <= 4; ++priority) for (uint32_t priority = 1; priority <= 4; ++priority)
{ {
uint64_t mult = m_wallet->get_fee_multiplier(priority); uint64_t mult = m_wallet->get_fee_multiplier(priority);
fees.push_back(per_kb_fee * typical_size_kb * mult); fees.push_back(base_fee * typical_size * mult);
} }
std::vector<std::pair<uint64_t, uint64_t>> blocks; std::vector<std::pair<uint64_t, uint64_t>> blocks;
try try
{ {
uint64_t base_size = typical_size_kb * 1024; uint64_t base_size = typical_size * size_granularity;
blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees); blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -1839,6 +1842,8 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
if (ring_size != 0 && ring_size != DEFAULT_MIX+1) if (ring_size != 0 && ring_size != DEFAULT_MIX+1)
message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
else if (ring_size == DEFAULT_MIX)
message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored.");
const auto pwd_container = get_and_verify_password(); const auto pwd_container = get_and_verify_password();
if (pwd_container) if (pwd_container)
@ -4704,6 +4709,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true; return true;
} }
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2; const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2;
if(local_args.size() < min_args) if(local_args.size() < min_args)
@ -5228,6 +5238,11 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true; return true;
} }
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
uint64_t unlock_block = 0; uint64_t unlock_block = 0;
if (locked) { if (locked) {
@ -5466,12 +5481,28 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
if (fake_outs_count == 0) if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIX; fake_outs_count = DEFAULT_MIX;
} }
else if (ring_size == 0)
{
fail_msg_writer() << tr("Ring size must not be 0");
return true;
}
else else
{ {
fake_outs_count = ring_size - 1; fake_outs_count = ring_size - 1;
local_args.erase(local_args.begin()); local_args.erase(local_args.begin());
} }
} }
uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
if (adjusted_fake_outs_count > fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
std::vector<uint8_t> extra; std::vector<uint8_t> extra;
bool payment_id_seen = false; bool payment_id_seen = false;

View File

@ -1297,6 +1297,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin(); size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
if (fake_outs_count == 0) if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIXIN; fake_outs_count = DEFAULT_MIXIN;
fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority)); uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority));

View File

@ -50,12 +50,13 @@ void NodeRPCProxy::invalidate()
m_height = 0; m_height = 0;
for (size_t n = 0; n < 256; ++n) for (size_t n = 0; n < 256; ++n)
m_earliest_height[n] = 0; m_earliest_height[n] = 0;
m_dynamic_per_kb_fee_estimate = 0; m_dynamic_base_fee_estimate = 0;
m_dynamic_per_kb_fee_estimate_cached_height = 0; m_dynamic_base_fee_estimate_cached_height = 0;
m_dynamic_per_kb_fee_estimate_grace_blocks = 0; m_dynamic_base_fee_estimate_grace_blocks = 0;
m_fee_quantization_mask = 1;
m_rpc_version = 0; m_rpc_version = 0;
m_target_height = 0; m_target_height = 0;
m_block_size_limit = 0; m_block_weight_limit = 0;
m_get_info_time = 0; m_get_info_time = 0;
} }
@ -99,7 +100,7 @@ boost::optional<std::string> NodeRPCProxy::get_info() const
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height");
m_height = resp_t.height; m_height = resp_t.height;
m_target_height = resp_t.target_height; m_target_height = resp_t.target_height;
m_block_size_limit = resp_t.block_size_limit; m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
m_get_info_time = now; m_get_info_time = now;
} }
return boost::optional<std::string>(); return boost::optional<std::string>();
@ -123,12 +124,12 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) c
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
boost::optional<std::string> NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const
{ {
auto res = get_info(); auto res = get_info();
if (res) if (res)
return res; return res;
block_size_limit = m_block_size_limit; block_weight_limit = m_block_weight_limit;
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
@ -153,7 +154,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const
{ {
uint64_t height; uint64_t height;
@ -161,10 +162,10 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
if (result) if (result)
return result; return result;
if (m_dynamic_per_kb_fee_estimate_cached_height != height || m_dynamic_per_kb_fee_estimate_grace_blocks != grace_blocks) if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{ {
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t); cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
req_t.grace_blocks = grace_blocks; req_t.grace_blocks = grace_blocks;
@ -173,12 +174,47 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
m_dynamic_per_kb_fee_estimate = resp_t.fee; m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_per_kb_fee_estimate_cached_height = height; m_dynamic_base_fee_estimate_cached_height = height;
m_dynamic_per_kb_fee_estimate_grace_blocks = grace_blocks; m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
m_fee_quantization_mask = resp_t.quantization_mask;
} }
fee = m_dynamic_per_kb_fee_estimate; fee = m_dynamic_base_fee_estimate;
return boost::optional<std::string>();
}
boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) const
{
uint64_t height;
boost::optional<std::string> result = get_height(height);
if (result)
return result;
if (m_dynamic_base_fee_estimate_cached_height != height)
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_base_fee_estimate_cached_height = height;
m_fee_quantization_mask = resp_t.quantization_mask;
}
fee_quantization_mask = m_fee_quantization_mask;
if (fee_quantization_mask == 0)
{
MERROR("Fee quantization mask is 0, forcing to 1");
fee_quantization_mask = 1;
}
return boost::optional<std::string>(); return boost::optional<std::string>();
} }

View File

@ -47,9 +47,10 @@ public:
boost::optional<std::string> get_height(uint64_t &height) const; boost::optional<std::string> get_height(uint64_t &height) const;
void set_height(uint64_t h); void set_height(uint64_t h);
boost::optional<std::string> get_target_height(uint64_t &height) const; boost::optional<std::string> get_target_height(uint64_t &height) const;
boost::optional<std::string> get_block_size_limit(uint64_t &block_size_limit) const; boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit) const;
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const; boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const;
boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const;
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask) const;
private: private:
boost::optional<std::string> get_info() const; boost::optional<std::string> get_info() const;
@ -59,12 +60,13 @@ private:
mutable uint64_t m_height; mutable uint64_t m_height;
mutable uint64_t m_earliest_height[256]; mutable uint64_t m_earliest_height[256];
mutable uint64_t m_dynamic_per_kb_fee_estimate; mutable uint64_t m_dynamic_base_fee_estimate;
mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; mutable uint64_t m_dynamic_base_fee_estimate_cached_height;
mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
mutable uint64_t m_fee_quantization_mask;
mutable uint32_t m_rpc_version; mutable uint32_t m_rpc_version;
mutable uint64_t m_target_height; mutable uint64_t m_target_height;
mutable uint64_t m_block_size_limit; mutable uint64_t m_block_weight_limit;
mutable time_t m_get_info_time; mutable time_t m_get_info_time;
}; };

View File

@ -84,8 +84,8 @@ using namespace cryptonote;
// used to choose when to stop adding outputs to a tx // used to choose when to stop adding outputs to a tx
#define APPROXIMATE_INPUT_BYTES 80 #define APPROXIMATE_INPUT_BYTES 80
// used to target a given block size (additional outputs may be added on top to build fee) // used to target a given block weight (additional outputs may be added on top to build fee)
#define TX_SIZE_TARGET(bytes) (bytes*2/3) #define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
// arbitrary, used to generate different hashes from the same input // arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c #define CHACHA8_KEY_TAIL 0x8c
@ -183,19 +183,21 @@ uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplie
return kB * fee_per_kb * fee_multiplier; return kB * fee_per_kb * fee_multiplier;
} }
uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, uint64_t fee_multiplier) uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{ {
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier); uint64_t fee = weight * base_fee * fee_multiplier;
fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
return fee;
} }
std::string get_size_string(size_t sz) std::string get_weight_string(size_t weight)
{ {
return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)"; return std::to_string(weight) + " weight";
} }
std::string get_size_string(const cryptonote::blobdata &tx) std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
{ {
return get_size_string(tx.size()); return get_weight_string(get_transaction_weight(tx, blob_size));
} }
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@ -576,7 +578,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// rangeSigs // rangeSigs
if (bulletproof) if (bulletproof)
size += ((2*6 + 4 + 5)*32 + 3) * n_outputs; {
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
}
else else
size += (2*64*32+32+64*32) * n_outputs; size += (2*64*32+32+64*32) * n_outputs;
@ -595,23 +602,63 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee // txnFee
size += 4; size += 4;
LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)"); LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size; return size;
} }
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{ {
if (use_rct) if (use_rct)
return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof); return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
else else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
} }
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
if (use_rct && bulletproof && n_outputs > 2)
{
const uint64_t bp_base = 368;
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
const uint64_t bp_size = 32 * (9 + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
}
return size;
}
uint8_t get_bulletproof_fork() uint8_t get_bulletproof_fork()
{ {
return 8; return 8;
} }
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
{
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
}
else
{
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
}
}
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
else
return calculate_fee(base_fee, blob_size, fee_multiplier);
}
crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{ {
crypto::hash8 payment_id8 = null_hash8; crypto::hash8 payment_id8 = null_hash8;
@ -822,14 +869,14 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon) bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{ {
m_rpc = rpc; m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype); m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected()) if(m_http_client.is_connected())
m_http_client.disconnect(); m_http_client.disconnect();
m_is_initialized = true; m_is_initialized = true;
m_upper_transaction_size_limit = upper_transaction_size_limit; m_upper_transaction_weight_limit = upper_transaction_weight_limit;
m_daemon_address = std::move(daemon_address); m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login); m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon; m_trusted_daemon = trusted_daemon;
@ -1142,10 +1189,9 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type) switch (rv.type)
{ {
case rct::RCTTypeSimple: case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull: case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
default: default:
LOG_ERROR("Unsupported rct type: " << rv.type); LOG_ERROR("Unsupported rct type: " << rv.type);
@ -5268,10 +5314,18 @@ 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()); LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx()); signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
if (sd.use_bulletproofs)
{
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;
}
crypto::secret_key tx_key; crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout; 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, sd.use_bulletproofs, 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, range_proof_type, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); 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, // 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, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
@ -5684,7 +5738,15 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx; cryptonote::transaction tx;
rct::multisig_out msout = ptx.multisig_sigs.front().msout; rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources; auto sources = sd.sources;
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, sd.use_bulletproofs, &msout, false); rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
if (sd.use_bulletproofs)
{
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;
}
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);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); 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), THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
@ -5784,9 +5846,18 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{ {
static const uint64_t old_multipliers[3] = {1, 2, 3}; static const struct
static const uint64_t new_multipliers[3] = {1, 20, 166}; {
static const uint64_t newer_multipliers[4] = {1, 4, 20, 166}; size_t count;
uint64_t multipliers[4];
}
multipliers[] =
{
{ 3, {1, 2, 3} },
{ 3, {1, 20, 166} },
{ 4, {1, 4, 20, 166} },
{ 4, {1, 5, 25, 1000} },
};
if (fee_algorithm == -1) if (fee_algorithm == -1)
fee_algorithm = get_fee_algorithm(); fee_algorithm = get_fee_algorithm();
@ -5802,47 +5873,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
priority = 1; priority = 1;
} }
THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
// 1 to 3/4 are allowed as priorities // 1 to 3/4 are allowed as priorities
uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3; const uint32_t max_priority = multipliers[fee_algorithm].count;
if (priority >= 1 && priority <= max_priority) if (priority >= 1 && priority <= max_priority)
{ {
switch (fee_algorithm) return multipliers[fee_algorithm].multipliers[priority-1];
{
case 0: return old_multipliers[priority-1];
case 1: return new_multipliers[priority-1];
case 2: return newer_multipliers[priority-1];
default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
}
} }
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1; return 1;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const uint64_t wallet2::get_dynamic_base_fee_estimate() const
{ {
uint64_t fee; uint64_t fee;
boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee); boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
if (!result) if (!result)
return fee; return fee;
LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB)); const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB;
return FEE_PER_KB; LOG_PRINT_L1("Failed to query base fee, using " << print_money(base_fee));
return base_fee;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_per_kb_fee() const uint64_t wallet2::get_base_fee() const
{ {
if(m_light_wallet) if(m_light_wallet)
{
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
return m_light_wallet_per_kb_fee / 1024;
else
return m_light_wallet_per_kb_fee; return m_light_wallet_per_kb_fee;
}
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
if (!use_dyn_fee) if (!use_dyn_fee)
return FEE_PER_KB; return FEE_PER_KB;
return get_dynamic_per_kb_fee_estimate(); return get_dynamic_base_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_quantization_mask() const
{
if(m_light_wallet)
{
return 1; // TODO
}
bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
if (!use_per_byte_fee)
return 1;
uint64_t fee_quantization_mask;
boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
if (result)
return 1;
return fee_quantization_mask;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
int wallet2::get_fee_algorithm() const int wallet2::get_fee_algorithm() const
{ {
// changes at v3 and v5 // changes at v3, v5, v8
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
return 3;
if (use_fork_rules(5, 0)) if (use_fork_rules(5, 0))
return 2; return 2;
if (use_fork_rules(3, -720 * 14)) if (use_fork_rules(3, -720 * 14))
@ -5850,19 +5942,39 @@ int wallet2::get_fee_algorithm() const
return 0; return 0;
} }
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
if (use_fork_rules(7, 10))
return 7;
if (use_fork_rules(6, 10))
return 5;
if (use_fork_rules(2, 10))
return 3;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_max_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::adjust_mixin(uint64_t mixin) const uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{ {
if (mixin < 6 && use_fork_rules(7, 10)) { const uint64_t min_ring_size = get_min_ring_size();
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7"); if (mixin + 1 < min_ring_size)
mixin = 6; {
MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
mixin = min_ring_size-1;
} }
else if (mixin < 4 && use_fork_rules(6, 10)) { const uint64_t max_ring_size = get_max_ring_size();
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); if (max_ring_size && mixin + 1 > max_ring_size)
mixin = 4; {
} MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
else if (mixin < 2 && use_fork_rules(2, 10)) { mixin = max_ring_size-1;
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
} }
return mixin; return mixin;
} }
@ -5874,7 +5986,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
try try
{ {
// check if there's a backlog in the tx pool // check if there's a backlog in the tx pool
const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024; const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(1);
const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)}); const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1) if (blocks.size() != 1)
{ {
@ -5888,10 +6003,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
} }
// get the current full reward zone // get the current full reward zone
uint64_t block_size_limit = 0; uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info"); throw_on_rpc_response_error(result, "get_info");
const uint64_t full_reward_zone = block_size_limit / 2; const uint64_t full_reward_zone = block_weight_limit / 2;
// get the last N block headers and sum the block sizes // get the last N block headers and sum the block sizes
const size_t N = 10; const size_t N = 10;
@ -5915,14 +6030,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
MERROR("Bad blockheaders size"); MERROR("Bad blockheaders size");
return priority; return priority;
} }
size_t block_size_sum = 0; size_t block_weight_sum = 0;
for (const cryptonote::block_header_response &i : getbh_res.headers) for (const cryptonote::block_header_response &i : getbh_res.headers)
{ {
block_size_sum += i.block_size; block_weight_sum += i.block_weight;
} }
// estimate how 'full' the last N blocks are // estimate how 'full' the last N blocks are
const size_t P = 100 * block_size_sum / (N * full_reward_zone); const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str()); MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
if (P > 80) if (P > 80)
{ {
@ -5948,8 +6063,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{ {
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true); const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// failsafe split attempt counter // failsafe split attempt counter
size_t attempt_count = 0; size_t attempt_count = 0;
@ -5976,13 +6093,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx; pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary. // loop until fee is met without increasing tx size to next KB boundary.
const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false); uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask);
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do do
{ {
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx); transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx); auto txBlob = t_serializable_object_to_blob(ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
} while (ptx.fee < needed_fee); } while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx); ptx_vector.push_back(ptx);
@ -6952,7 +7068,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money)); LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
@ -7054,10 +7170,10 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout; rct::multisig_out msout;
LOG_PRINT_L2("constructing tx"); 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, false, 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, rct::RangeProofBulletproof, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r); 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(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images; std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
@ -7103,13 +7219,13 @@ 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, 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, 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, bool bulletproof) uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type)
{ {
using namespace cryptonote; using namespace cryptonote;
// throw if attempting a transaction with no destinations // throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money)); LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " ")); LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
@ -7259,10 +7375,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
rct::multisig_out msout; rct::multisig_out msout;
LOG_PRINT_L2("constructing tx"); LOG_PRINT_L2("constructing tx");
auto sources_copy = sources; 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, bulletproof, 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, range_proof_type, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r); 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(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
// work out the permutation done on sources // work out the permutation done on sources
std::vector<size_t> ins_order; std::vector<size_t> ins_order;
@ -7304,10 +7420,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("Creating supplementary multisig transaction"); LOG_PRINT_L2("Creating supplementary multisig transaction");
cryptonote::transaction ms_tx; cryptonote::transaction ms_tx;
auto sources_copy_copy = sources_copy; 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, bulletproof, &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, range_proof_type, &msout, false);
LOG_PRINT_L2("constructed tx, r="<<r); 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(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix"); THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout}); multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
@ -7975,11 +8091,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<cryptonote::tx_destination_entry> dsts; std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx; cryptonote::transaction tx;
pending_tx ptx; pending_tx ptx;
size_t bytes; size_t weight;
uint64_t needed_fee; uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs; std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
TX() : bytes(0), needed_fee(0) {} 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 account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations) if (merge_destinations)
@ -8007,12 +8123,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<TX> txes; std::vector<TX> txes;
bool adding_fee; // true if new outputs go towards fee, rather than destinations bool adding_fee; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee, available_for_fee = 0; uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
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 use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations // throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@ -8043,7 +8162,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway // early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation // we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through // ever changes, this might be missed, so let this go through
const uint64_t min_fee = (fee_multiplier * fee_per_kb * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024; const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
uint64_t balance_subtotal = 0; uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0; uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices) for (uint32_t index_minor : subaddr_indices)
@ -8061,11 +8180,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i); LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount // determine threshold for fractional amount
const size_t tx_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof); const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof); const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!"); THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring; const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024; const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
// gather all dust and non-dust outputs belonging to specified subaddresses // gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0; size_t num_nondust_outputs = 0;
@ -8158,7 +8277,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{ {
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee. // will get us a known fee.
uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier); uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices); preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty()) if (!preferred_inputs.empty())
{ {
@ -8200,7 +8319,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
TX &tx = txes.back(); TX &tx = txes.back();
LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size()); LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " ")); LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " ")); LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount))); LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
@ -8263,7 +8382,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
} }
else else
{ {
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{ {
// we can fully pay that destination // 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) << LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@ -8275,7 +8394,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index; ++original_output_index;
} }
if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination // 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) << 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)); " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@ -8287,7 +8406,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// here, check if we need to sent tx and start a new one // here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit); << upper_transaction_weight_limit);
bool try_tx = false; bool try_tx = false;
// if we have preferred picks, but haven't yet used all of them, continue // if we have preferred picks, but haven't yet used all of them, continue
if (preferred_inputs.empty()) if (preferred_inputs.empty())
@ -8299,8 +8418,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
} }
else else
{ {
const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
} }
} }
@ -8308,8 +8427,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx; cryptonote::transaction test_tx;
pending_tx test_ptx; pending_tx test_ptx;
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
uint64_t inputs = 0, outputs = needed_fee; uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount(); for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@ -8326,14 +8444,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.selected_transfers.size() << " inputs"); tx.selected_transfers.size() << " inputs");
if (use_rct) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof); test_tx, test_ptx, range_proof_type);
else else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, 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); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0); available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" << LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)"); print_money(needed_fee) << " needed)");
if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0) if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
@ -8369,22 +8487,22 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while (needed_fee > test_ptx.fee) { while (needed_fee > test_ptx.fee) {
if (use_rct) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof); test_tx, test_ptx, range_proof_type);
else else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, 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); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx); txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
} }
LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs; tx.outs = outs;
tx.needed_fee = needed_fee; tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee; accumulated_fee += test_ptx.fee;
@ -8442,7 +8560,7 @@ skip_tx:
extra, /* const std::vector<uint8_t>& extra, */ extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */ test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */ test_ptx, /* OUT cryptonote::transaction& tx, */
bulletproof); range_proof_type);
} else { } else {
transfer_selected(tx.dsts, transfer_selected(tx.dsts,
tx.selected_transfers, tx.selected_transfers,
@ -8459,7 +8577,7 @@ skip_tx:
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
} }
std::vector<wallet2::pending_tx> ptx_vector; std::vector<wallet2::pending_tx> ptx_vector;
@ -8470,7 +8588,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers) for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount(); tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
" " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " << " outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx); ptx_vector.push_back(tx.ptx);
@ -8568,21 +8686,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<cryptonote::tx_destination_entry> dsts; std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx; cryptonote::transaction tx;
pending_tx ptx; pending_tx ptx;
size_t bytes; size_t weight;
uint64_t needed_fee; uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs; std::vector<std::vector<get_outs_entry>> outs;
TX() : bytes(0), needed_fee(0) {} TX() : weight(0), needed_fee(0) {}
}; };
std::vector<TX> txes; std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0; uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
std::vector<std::vector<get_outs_entry>> outs; std::vector<std::vector<get_outs_entry>> outs;
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 use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const uint64_t fee_per_kb = get_per_kb_fee(); const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@ -8604,7 +8725,25 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// get a random unspent output and use it to pay next chunk. We try to alternate // get a random unspent output and use it to pay next chunk. We try to alternate
// dust and non dust to ensure we never get with only dust, from which we might // dust and non dust to ensure we never get with only dust, from which we might
// get a tx that can't pay for itself // get a tx that can't pay for itself
size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_per_kb * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers); uint64_t fee_dust_threshold;
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
{
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
}
else
{
fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
}
size_t idx =
unused_transfers_indices.empty()
? pop_best_value(unused_dust_indices, tx.selected_transfers)
: unused_dust_indices.empty()
? pop_best_value(unused_transfers_indices, tx.selected_transfers)
: ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
? pop_best_value(unused_dust_indices, tx.selected_transfers)
: pop_best_value(unused_transfers_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx]; const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@ -8619,16 +8758,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one // here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit); << upper_transaction_weight_limit);
const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof); const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) { if (try_tx) {
cryptonote::transaction test_tx; cryptonote::transaction test_tx;
pending_tx test_ptx; pending_tx test_ptx;
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@ -8636,14 +8774,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs"); tx.selected_transfers.size() << " outputs");
if (use_rct) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof); test_tx, test_ptx, range_proof_type);
else else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, 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); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" << LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)"); print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@ -8653,22 +8791,22 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_fee; tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof); test_tx, test_ptx, range_proof_type);
else else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, 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); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx); txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
} while (needed_fee > test_ptx.fee); } while (needed_fee > test_ptx.fee);
LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs; tx.outs = outs;
tx.needed_fee = needed_fee; tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee; accumulated_fee += test_ptx.fee;
@ -8692,7 +8830,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx; pending_tx test_ptx;
if (use_rct) { if (use_rct) {
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
test_tx, test_ptx, bulletproof); test_tx, test_ptx, range_proof_type);
} else { } else {
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, 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); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@ -8700,7 +8838,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
} }
std::vector<wallet2::pending_tx> ptx_vector; std::vector<wallet2::pending_tx> ptx_vector;
@ -8711,7 +8849,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers) for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount(); tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
" " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " << " outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx); ptx_vector.push_back(tx.ptx);
@ -8746,11 +8884,14 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
return close_enough; return close_enough;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_upper_transaction_size_limit() const uint64_t wallet2::get_upper_transaction_weight_limit() const
{ {
if (m_upper_transaction_size_limit > 0) if (m_upper_transaction_weight_limit > 0)
return m_upper_transaction_size_limit; return m_upper_transaction_weight_limit;
uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
if (use_fork_rules(8, 10))
return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
else
return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -8857,16 +8998,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_unmixable_outputs() std::vector<size_t> wallet2::select_available_unmixable_outputs()
{ {
// request all outputs with less than 3 instances // request all outputs with less instances than the min ring size
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
return select_available_outputs_from_histogram(min_mixin + 1, false, true, false);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs() std::vector<size_t> wallet2::select_available_mixable_outputs()
{ {
// request all outputs with at least 3 instances, so we can use mixin 2 with // request all outputs with at least as many instances as the min ring size
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
return select_available_outputs_from_histogram(min_mixin + 1, true, true, true);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions() std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
@ -8875,7 +9014,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD); tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
// may throw // may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(); std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@ -8890,7 +9029,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs; std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
for (auto n: unmixable_outputs) for (auto n: unmixable_outputs)
{ {
if (m_transfers[n].amount() < fee_per_kb) if (m_transfers[n].amount() < base_fee)
unmixable_dust_outputs.push_back(n); unmixable_dust_outputs.push_back(n);
else else
unmixable_transfer_outputs.push_back(n); unmixable_transfer_outputs.push_back(n);
@ -9283,6 +9422,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
const rct::key C = tx.rct_signatures.outPk[n].mask; const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp; rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
if (rct::equalKeys(C, Ctmp)) if (rct::equalKeys(C, Ctmp))
amount = rct::h2d(ecdh_info.amount); amount = rct::h2d(ecdh_info.amount);
@ -9853,7 +9994,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const
// Calculated blockchain height // Calculated blockchain height
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
// testnet got some huge rollbacks, so the estimation is way off // testnet got some huge rollbacks, so the estimation is way off
static const uint64_t approximate_testnet_rolled_back_blocks = 148540; static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks) if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
approx_blockchain_height -= approximate_testnet_rolled_back_blocks; approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
@ -11211,46 +11352,46 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
uint64_t block_size_limit = 0; uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info"); throw_on_rpc_response_error(result, "get_info");
uint64_t full_reward_zone = block_size_limit / 2; uint64_t full_reward_zone = block_weight_limit / 2;
THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon"); THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
std::vector<std::pair<uint64_t, uint64_t>> blocks; std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels) for (const auto &fee_level: fee_levels)
{ {
const double our_fee_byte_min = fee_level.first; const double our_fee_byte_min = fee_level.first;
const double our_fee_byte_max = fee_level.second; const double our_fee_byte_max = fee_level.second;
uint64_t priority_size_min = 0, priority_size_max = 0; uint64_t priority_weight_min = 0, priority_weight_max = 0;
for (const auto &i: res.backlog) for (const auto &i: res.backlog)
{ {
if (i.blob_size == 0) if (i.weight == 0)
{ {
MWARNING("Got 0 sized blob from txpool, ignored"); MWARNING("Got 0 weight tx from txpool, ignored");
continue; continue;
} }
double this_fee_byte = i.fee / (double)i.blob_size; double this_fee_byte = i.fee / (double)i.weight;
if (this_fee_byte >= our_fee_byte_min) if (this_fee_byte >= our_fee_byte_min)
priority_size_min += i.blob_size; priority_weight_min += i.weight;
if (this_fee_byte >= our_fee_byte_max) if (this_fee_byte >= our_fee_byte_max)
priority_size_max += i.blob_size; priority_weight_max += i.weight;
} }
uint64_t nblocks_min = priority_size_min / full_reward_zone; uint64_t nblocks_min = priority_weight_min / full_reward_zone;
uint64_t nblocks_max = priority_size_max / full_reward_zone; uint64_t nblocks_max = priority_weight_max / full_reward_zone;
MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for " MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
<< our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, " << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
<< nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone); << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
blocks.push_back(std::make_pair(nblocks_min, nblocks_max)); blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
} }
return blocks; return blocks;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees) std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees)
{ {
THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
for (uint64_t fee: fees) for (uint64_t fee: fees)
{ {
THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
@ -11258,7 +11399,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
std::vector<std::pair<double, double>> fee_levels; std::vector<std::pair<double, double>> fee_levels;
for (uint64_t fee: fees) for (uint64_t fee: fees)
{ {
double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size; double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max); fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
} }
return estimate_backlog(fee_levels); return estimate_backlog(fee_levels);

View File

@ -630,14 +630,9 @@ namespace tools
void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;}
bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;}
// upper_transaction_size_limit as defined below is set to
// approximately 125% of the fixed minimum allowable penalty
// free block size. TODO: fix this so that it actually takes
// into account the current median block size rather than
// the minimum block size.
bool deinit(); bool deinit();
bool init(bool rpc, std::string daemon_address = "http://localhost:8080", bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false); boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); } void stop() { m_run.store(false, std::memory_order_relaxed); }
@ -729,7 +724,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); 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, 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, 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, bool bulletproof); uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type);
void commit_tx(pending_tx& ptx_vector); void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector); void commit_tx(std::vector<pending_tx>& ptx_vector);
@ -1084,10 +1079,13 @@ namespace tools
bool is_synced() const; bool is_synced() const;
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
uint64_t get_per_kb_fee() const; uint64_t get_base_fee() const;
uint64_t get_fee_quantization_mask() const;
uint64_t get_min_ring_size() const;
uint64_t get_max_ring_size() const;
uint64_t adjust_mixin(uint64_t mixin) const; uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority); uint32_t adjust_priority(uint32_t priority);
@ -1212,9 +1210,9 @@ namespace tools
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit() const; uint64_t get_upper_transaction_weight_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const; std::vector<uint64_t> get_unspent_amounts_vector() const;
uint64_t get_dynamic_per_kb_fee_estimate() const; uint64_t get_dynamic_base_fee_estimate() const;
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const; std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height); void set_spent(size_t idx, uint64_t height);
@ -1269,7 +1267,7 @@ namespace tools
std::unordered_map<std::string, std::string> m_attributes; std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book; std::vector<tools::wallet2::address_book_row> m_address_book;
std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags; std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info; const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k; const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@ -1838,7 +1836,7 @@ namespace tools
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
// calculate total amount being sent to all destinations // calculate total amount being sent to all destinations
@ -1968,9 +1966,9 @@ namespace tools
crypto::secret_key tx_key; crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout; rct::multisig_out msout;
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, false, 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, rct::RangeProofBorromean, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images; std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool

View File

@ -679,30 +679,30 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct tx_too_big : public transfer_error struct tx_too_big : public transfer_error
{ {
explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_size_limit) explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit)
: transfer_error(std::move(loc), "transaction is too big") : transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx) , m_tx(tx)
, m_tx_size_limit(tx_size_limit) , m_tx_weight_limit(tx_weight_limit)
{ {
} }
const cryptonote::transaction& tx() const { return m_tx; } const cryptonote::transaction& tx() const { return m_tx; }
uint64_t tx_size_limit() const { return m_tx_size_limit; } uint64_t tx_weight_limit() const { return m_tx_weight_limit; }
std::string to_string() const std::string to_string() const
{ {
std::ostringstream ss; std::ostringstream ss;
cryptonote::transaction tx = m_tx; cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() << ss << transfer_error::to_string() <<
", tx_size_limit = " << m_tx_size_limit << ", tx_weight_limit = " << m_tx_weight_limit <<
", tx size = " << get_object_blobsize(m_tx) << ", tx weight = " << get_transaction_weight(m_tx) <<
", tx:\n" << cryptonote::obj_to_json_str(tx); ", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str(); return ss.str();
} }
private: private:
cryptonote::transaction m_tx; cryptonote::transaction m_tx;
uint64_t m_tx_size_limit; uint64_t m_tx_weight_limit;
}; };
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error struct zero_destination : public transfer_error

View File

@ -1542,7 +1542,6 @@ namespace tools
rpc_transfers.spent = td.m_spent; rpc_transfers.spent = td.m_spent;
rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.global_index = td.m_global_output_index;
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
rpc_transfers.subaddr_index = td.m_subaddr_index.minor; rpc_transfers.subaddr_index = td.m_subaddr_index.minor;
rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
res.transfers.push_back(rpc_transfers); res.transfers.push_back(rpc_transfers);

View File

@ -865,7 +865,6 @@ namespace wallet_rpc
bool spent; bool spent;
uint64_t global_index; uint64_t global_index;
std::string tx_hash; std::string tx_hash;
uint64_t tx_size;
uint32_t subaddr_index; uint32_t subaddr_index;
std::string key_image; std::string key_image;
@ -874,7 +873,6 @@ namespace wallet_rpc
KV_SERIALIZE(spent) KV_SERIALIZE(spent)
KV_SERIALIZE(global_index) KV_SERIALIZE(global_index)
KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_size)
KV_SERIALIZE(subaddr_index) KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(key_image) KV_SERIALIZE(key_image)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()

View File

@ -41,7 +41,8 @@ set(core_tests_sources
transaction_tests.cpp transaction_tests.cpp
tx_validation.cpp tx_validation.cpp
v2_tests.cpp v2_tests.cpp
rct.cpp) rct.cpp
bulletproofs.cpp)
set(core_tests_headers set(core_tests_headers
block_reward.h block_reward.h
@ -58,7 +59,8 @@ set(core_tests_headers
transaction_tests.h transaction_tests.h
tx_validation.h tx_validation.h
v2_tests.h v2_tests.h
rct.h) rct.h
bulletproofs.h)
add_executable(core_tests add_executable(core_tests
${core_tests_sources} ${core_tests_sources}
@ -73,6 +75,7 @@ target_link_libraries(core_tests
device device
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})
enable_stack_trace(core_tests)
set_property(TARGET core_tests set_property(TARGET core_tests
PROPERTY PROPERTY
FOLDER "tests") FOLDER "tests")

View File

@ -36,24 +36,24 @@ using namespace cryptonote;
namespace namespace
{ {
bool construct_miner_tx_by_size(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins, bool construct_miner_tx_by_weight(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins,
const account_public_address& miner_address, std::vector<size_t>& block_sizes, size_t target_tx_size, const account_public_address& miner_address, std::vector<size_t>& block_weights, size_t target_tx_weight,
size_t target_block_size, uint64_t fee = 0) size_t target_block_weight, uint64_t fee = 0)
{ {
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, fee, miner_address, miner_tx)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, fee, miner_address, miner_tx))
return false; return false;
size_t current_size = get_object_blobsize(miner_tx); size_t current_weight = get_transaction_weight(miner_tx);
size_t try_count = 0; size_t try_count = 0;
while (target_tx_size != current_size) while (target_tx_weight != current_weight)
{ {
++try_count; ++try_count;
if (10 < try_count) if (10 < try_count)
return false; return false;
if (target_tx_size < current_size) if (target_tx_weight < current_weight)
{ {
size_t diff = current_size - target_tx_size; size_t diff = current_weight - target_tx_weight;
if (diff <= miner_tx.extra.size()) if (diff <= miner_tx.extra.size())
miner_tx.extra.resize(miner_tx.extra.size() - diff); miner_tx.extra.resize(miner_tx.extra.size() - diff);
else else
@ -61,28 +61,28 @@ namespace
} }
else else
{ {
size_t diff = target_tx_size - current_size; size_t diff = target_tx_weight - current_weight;
miner_tx.extra.resize(miner_tx.extra.size() + diff); miner_tx.extra.resize(miner_tx.extra.size() + diff);
} }
current_size = get_object_blobsize(miner_tx); current_weight = get_transaction_weight(miner_tx);
} }
return true; return true;
} }
bool construct_max_size_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account, bool construct_max_weight_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account,
size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW) size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW)
{ {
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_prev), median_block_count); generator.get_last_n_block_weights(block_weights, get_block_hash(blk_prev), median_block_count);
size_t median = misc_utils::median(block_sizes); size_t median = misc_utils::median(block_weights);
median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)); median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1));
transaction miner_tx; transaction miner_tx;
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev), bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev),
miner_account.get_keys().m_account_address, block_sizes, 2 * median, 2 * median); miner_account.get_keys().m_account_address, block_weights, 2 * median, 2 * median);
if (!r) if (!r)
return false; return false;
@ -97,7 +97,7 @@ namespace
for (size_t i = 0; i < block_count; ++i) for (size_t i = 0; i < block_count; ++i)
{ {
block blk_i; block blk_i;
if (!construct_max_size_block(generator, blk_i, blk, miner_account)) if (!construct_max_weight_block(generator, blk_i, blk, miner_account))
return false; return false;
events.push_back(blk_i); events.push_back(blk_i);
@ -141,18 +141,18 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
// Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks
DO_CALLBACK(events, "mark_invalid_block"); DO_CALLBACK(events, "mark_invalid_block");
block blk_1_bad_1; block blk_1_bad_1;
if (!construct_max_size_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1)) if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1))
return false; return false;
events.push_back(blk_1_bad_1); events.push_back(blk_1_bad_1);
DO_CALLBACK(events, "mark_invalid_block"); DO_CALLBACK(events, "mark_invalid_block");
block blk_1_bad_2; block blk_1_bad_2;
if (!construct_max_size_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1)) if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1))
return false; return false;
events.push_back(blk_1_bad_2); events.push_back(blk_1_bad_2);
block blk_1; block blk_1;
if (!construct_max_size_block(generator, blk_1, blk_0r, miner_account)) if (!construct_max_weight_block(generator, blk_1, blk_0r, miner_account))
return false; return false;
events.push_back(blk_1); events.push_back(blk_1);
@ -187,16 +187,16 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
{ {
transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE); transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE);
transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE); transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE);
size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(tx_2); size_t txs_1_weight = get_transaction_weight(tx_1) + get_transaction_weight(tx_2);
uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2); uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW); generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
size_t median = misc_utils::median(block_sizes); size_t median = misc_utils::median(block_weights);
transaction miner_tx; transaction miner_tx;
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7), bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7),
miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee); miner_account.get_keys().m_account_address, block_weights, 2 * median - txs_1_weight, 2 * median, txs_fee);
if (!r) if (!r)
return false; return false;
@ -206,7 +206,7 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
block blk_8; block blk_8;
generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes, generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes,
0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_size); 0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight);
events.push_back(blk_8); events.push_back(blk_8);
DO_CALLBACK(events, "mark_checked_block"); DO_CALLBACK(events, "mark_checked_block");

View File

@ -586,11 +586,11 @@ bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& ev
block blk_test; block blk_test;
std::vector<crypto::hash> tx_hashes; std::vector<crypto::hash> tx_hashes;
tx_hashes.push_back(get_transaction_hash(tx_0)); tx_hashes.push_back(get_transaction_hash(tx_0));
size_t txs_size = get_object_blobsize(tx_0); size_t txs_weight = get_transaction_weight(tx_0);
diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1);
if (!generator.construct_block_manually(blk_test, blk_last, miner_account, if (!generator.construct_block_manually(blk_test, blk_last, miner_account,
test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp, test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp,
crypto::hash(), diffic, transaction(), tx_hashes, txs_size)) crypto::hash(), diffic, transaction(), tx_hashes, txs_weight))
return false; return false;
blobdata blob = t_serializable_object_to_blob(blk_test); blobdata blob = t_serializable_object_to_blob(blk_test);

View File

@ -0,0 +1,361 @@
// 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
#include "ringct/rctSigs.h"
#include "ringct/bulletproofs.h"
#include "chaingen.h"
#include "bulletproofs.h"
#include "device/device.hpp"
using namespace epee;
using namespace crypto;
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,
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
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
// create 12 miner accounts, and have them mine the next 12 blocks
cryptonote::account_base miner_accounts[12];
const cryptonote::block *prev_block = &blk_0;
cryptonote::block blocks[12 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
for (size_t n = 0; n < 12; ++n) {
miner_accounts[n].generate();
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
false, "Failed to generate block");
events.push_back(blocks[n]);
prev_block = blocks + n;
LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
}
// rewind
cryptonote::block blk_r, blk_last;
{
blk_last = blocks[11];
for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
{
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[12+i], blk_last, miner_account,
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
false, "Failed to generate block");
events.push_back(blocks[12+i]);
blk_last = blocks[12+i];
}
blk_r = blk_last;
}
// create 4 txes from these miners in another block, to generate some rct outputs
std::vector<transaction> rct_txes;
cryptonote::block blk_txes;
std::vector<crypto::hash> starting_rct_tx_hashes;
static const uint64_t input_amounts_available[] = {5000000000000, 30000000000000, 100000000000, 80000000000};
for (size_t n = 0; n < n_txes; ++n)
{
std::vector<tx_source_entry> sources;
sources.resize(1);
tx_source_entry& src = sources.back();
const uint64_t needed_amount = input_amounts_available[n];
src.amount = input_amounts_available[n];
size_t real_index_in_tx = 0;
for (size_t m = 0; m <= mixin; ++m) {
size_t index_in_tx = 0;
for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i)
if (blocks[m].miner_tx.vout[i].amount == needed_amount)
index_in_tx = i;
CHECK_AND_ASSERT_MES(blocks[m].miner_tx.vout[index_in_tx].amount == needed_amount, false, "Expected amount not found");
src.push_output(m, boost::get<txout_to_key>(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount);
if (m == n)
real_index_in_tx = index_in_tx;
}
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx);
src.real_output = n;
src.real_output_in_tx_index = real_index_in_tx;
src.mask = rct::identity();
src.rct = false;
//fill outputs entry
tx_destination_entry td;
td.addr = miner_accounts[n].get_keys().m_account_address;
std::vector<tx_destination_entry> destinations;
for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o)
{
td.amount = amounts_paid[o];
destinations.push_back(td);
}
if (pre_tx && !pre_tx(sources, destinations, n))
{
MDEBUG("pre_tx returned failure");
return false;
}
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
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]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
{
MDEBUG("post_tx returned failure");
return false;
}
//events.push_back(rct_txes.back());
starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes.back()));
LOG_PRINT_L0("Test tx: " << obj_to_json_str(rct_txes.back()));
for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o)
{
crypto::key_derivation derivation;
bool r = crypto::generate_key_derivation(destinations[o].addr.m_view_public_key, tx_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
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)
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"));
}
while (amounts_paid[0] != (size_t)-1)
++amounts_paid;
++amounts_paid;
}
if (!valid)
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(rct_txes);
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),
false, "Failed to generate block");
if (!valid)
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_txes);
blk_last = blk_txes;
return true;
}
bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const
{
DEFINE_TESTS_ERROR_CONTEXT(context);
CHECK_TEST_CONDITION(tx.version >= 2);
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
size_t n_sizes = 0, n_amounts = 0;
for (size_t n = 0; n < tx_idx; ++n)
{
while (sizes[0] != (size_t)-1)
++sizes;
++sizes;
}
while (sizes[n_sizes] != (size_t)-1)
n_amounts += sizes[n_sizes++];
CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == n_sizes);
CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts);
for (size_t n = 0; n < n_sizes; ++n)
CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]);
return true;
}
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"); });
}
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);
}
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"); });
}
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"); });
}
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"); });
}
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);
}
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);
}
bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
{
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"); });
}
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);
}
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 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"); });
}
bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const
{
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);
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());
return true;
});
}
bool gen_bp_tx_invalid_empty_proofs::generate(std::vector<test_event_entry>& events) const
{
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);
tx.rct_signatures.p.bulletproofs.clear();
return true;
});
}
bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& events) const
{
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);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back());
return true;
});
}
bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& events) const
{
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);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen());
return true;
});
}
bool gen_bp_tx_invalid_borromean_type::generate(std::vector<test_event_entry>& events) const
{
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;
return true;
});
}

View File

@ -0,0 +1,193 @@
// 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 "chaingen.h"
struct gen_bp_tx_validation_base : public test_chain_unit_base
{
gen_bp_tx_validation_base()
: m_invalid_tx_index(0)
, m_invalid_block_index(0)
{
REGISTER_CALLBACK_METHOD(gen_bp_tx_validation_base, mark_invalid_tx);
REGISTER_CALLBACK_METHOD(gen_bp_tx_validation_base, mark_invalid_block);
}
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/)
{
if (m_invalid_tx_index == event_idx)
return tvc.m_verifivation_failed;
else
return !tvc.m_verifivation_failed && tx_added;
}
bool check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_idx, const std::vector<cryptonote::transaction>& /*txs*/)
{
size_t failed = 0;
for (const cryptonote::tx_verification_context &tvc: tvcs)
if (tvc.m_verifivation_failed)
++failed;
if (m_invalid_tx_index == event_idx)
return failed > 0;
else
return failed == 0 && tx_added == tvcs.size();
}
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/)
{
if (m_invalid_block_index == event_idx)
return bvc.m_verifivation_failed;
else
return !bvc.m_verifivation_failed;
}
bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
{
m_invalid_block_index = ev_index + 1;
return true;
}
bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
{
m_invalid_tx_index = ev_index + 1;
return true;
}
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,
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;
bool check_bp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const;
private:
size_t m_invalid_tx_index;
size_t m_invalid_block_index;
};
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 cryptonote::test_options test_options = {
hard_forks
};
};
// valid
struct gen_bp_tx_valid_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_valid_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_1_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_1_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_valid_2>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_valid_3 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_valid_3>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_valid_16 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_valid_16>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_4_2_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_4_2_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_16_16 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_16_16>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_txs_valid_2_and_2>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_txs_invalid_2_and_8_2_and_16_16_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_txs_valid_2_and_3_and_2_and_4 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_txs_valid_2_and_3_and_2_and_4>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_not_enough_proofs>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_empty_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_empty_proofs>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_too_many_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_too_many_proofs>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_wrong_amount : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_wrong_amount>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_borromean_type : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_borromean_type>: public get_test_options<gen_bp_tx_validation_base> {};

View File

@ -69,13 +69,13 @@ void test_generator::get_block_chain(std::vector<block_info>& blockchain, const
std::reverse(blockchain.begin(), blockchain.end()); std::reverse(blockchain.begin(), blockchain.end());
} }
void test_generator::get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const void test_generator::get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const
{ {
std::vector<block_info> blockchain; std::vector<block_info> blockchain;
get_block_chain(blockchain, head, n); get_block_chain(blockchain, head, n);
BOOST_FOREACH(auto& bi, blockchain) BOOST_FOREACH(auto& bi, blockchain)
{ {
block_sizes.push_back(bi.block_size); block_weights.push_back(bi.block_weight);
} }
} }
@ -95,17 +95,17 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl
return get_already_generated_coins(blk_hash); return get_already_generated_coins(blk_hash);
} }
void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, uint8_t hf_version) void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, std::vector<size_t>& block_weights, uint64_t already_generated_coins, uint8_t hf_version)
{ {
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); const size_t block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
uint64_t block_reward; uint64_t block_reward;
get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version); get_block_reward(misc_utils::median(block_weights), block_weight, already_generated_coins, block_reward, hf_version);
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_weight);
} }
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list) std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list)
{ {
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
@ -121,52 +121,52 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
} }
uint64_t total_fee = 0; uint64_t total_fee = 0;
size_t txs_size = 0; size_t txs_weight = 0;
BOOST_FOREACH(auto& tx, tx_list) BOOST_FOREACH(auto& tx, tx_list)
{ {
uint64_t fee = 0; uint64_t fee = 0;
bool r = get_tx_fee(tx, fee); bool r = get_tx_fee(tx, fee);
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
total_fee += fee; total_fee += fee;
txs_size += get_object_blobsize(tx); txs_weight += get_transaction_weight(tx);
} }
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
while (true) while (true)
{ {
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10))
return false; return false;
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (target_block_size < actual_block_size) if (target_block_weight < actual_block_weight)
{ {
target_block_size = actual_block_size; target_block_weight = actual_block_weight;
} }
else if (actual_block_size < target_block_size) else if (actual_block_weight < target_block_weight)
{ {
size_t delta = target_block_size - actual_block_size; size_t delta = target_block_weight - actual_block_weight;
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_size == target_block_size) if (actual_block_weight == target_block_weight)
{ {
break; break;
} }
else else
{ {
CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size"); CHECK_AND_ASSERT_MES(target_block_weight < actual_block_weight, false, "Unexpected block size");
delta = actual_block_size - target_block_size; delta = actual_block_weight - target_block_weight;
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_size == target_block_size) if (actual_block_weight == target_block_weight)
{ {
break; break;
} }
else else
{ {
CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, false, "Unexpected block size"); CHECK_AND_ASSERT_MES(actual_block_weight < target_block_weight, false, "Unexpected block size");
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
target_block_size = txs_size + get_object_blobsize(blk.miner_tx); target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
} }
} }
} }
@ -183,16 +183,16 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
blk.timestamp++; blk.timestamp++;
add_block(blk, txs_size, block_sizes, already_generated_coins); add_block(blk, txs_weight, block_weights, already_generated_coins);
return true; return true;
} }
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp) bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp)
{ {
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
std::list<cryptonote::transaction> tx_list; std::list<cryptonote::transaction> tx_list;
return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list); return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_weights, tx_list);
} }
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
@ -204,10 +204,10 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::b
// Keep difficulty unchanged // Keep difficulty unchanged
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
uint64_t already_generated_coins = get_already_generated_coins(prev_id); uint64_t already_generated_coins = get_already_generated_coins(prev_id);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list); return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list);
} }
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc,
@ -216,7 +216,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/, const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/,
const transaction& miner_tx/* = transaction()*/, const transaction& miner_tx/* = transaction()*/,
const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/, const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/,
size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/) size_t txs_weight/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/)
{ {
blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION; blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION; blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION;
@ -228,17 +228,17 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
size_t height = get_block_height(prev_block) + 1; size_t height = get_block_height(prev_block) + 1;
uint64_t already_generated_coins = get_already_generated_coins(prev_block); uint64_t already_generated_coins = get_already_generated_coins(prev_block);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_block_weights(block_weights, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (actual_params & bf_miner_tx) if (actual_params & bf_miner_tx)
{ {
blk.miner_tx = miner_tx; blk.miner_tx = miner_tx;
} }
else else
{ {
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, current_block_weight, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version))
return false; return false;
} }
@ -247,16 +247,16 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty();
fill_nonce(blk, a_diffic, height); fill_nonce(blk, a_diffic, height);
add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version); add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version);
return true; return true;
} }
bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block, bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
const cryptonote::account_base& miner_acc, const cryptonote::account_base& miner_acc,
const std::vector<crypto::hash>& tx_hashes, size_t txs_size) const std::vector<crypto::hash>& tx_hashes, size_t txs_weight)
{ {
return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size); return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight);
} }

View File

@ -135,7 +135,7 @@ VARIANT_TAG(binary_archive, serialized_block, 0xcd);
VARIANT_TAG(binary_archive, serialized_transaction, 0xce); VARIANT_TAG(binary_archive, serialized_transaction, 0xce);
VARIANT_TAG(binary_archive, event_visitor_settings, 0xcf); VARIANT_TAG(binary_archive, event_visitor_settings, 0xcf);
typedef boost::variant<cryptonote::block, cryptonote::transaction, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry; typedef boost::variant<cryptonote::block, cryptonote::transaction, std::vector<cryptonote::transaction>, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry;
typedef std::unordered_map<crypto::hash, const cryptonote::transaction*> map_hash2tx_t; typedef std::unordered_map<crypto::hash, const cryptonote::transaction*> map_hash2tx_t;
class test_chain_unit_base class test_chain_unit_base
@ -159,20 +159,20 @@ public:
block_info() block_info()
: prev_id() : prev_id()
, already_generated_coins(0) , already_generated_coins(0)
, block_size(0) , block_weight(0)
{ {
} }
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size) block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_weight)
: prev_id(a_prev_id) : prev_id(a_prev_id)
, already_generated_coins(an_already_generated_coins) , already_generated_coins(an_already_generated_coins)
, block_size(a_block_size) , block_weight(a_block_weight)
{ {
} }
crypto::hash prev_id; crypto::hash prev_id;
uint64_t already_generated_coins; uint64_t already_generated_coins;
size_t block_size; size_t block_weight;
}; };
enum block_fields enum block_fields
@ -190,15 +190,15 @@ public:
}; };
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const; void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
void get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const;
uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const;
uint64_t get_already_generated_coins(const cryptonote::block& blk) const; uint64_t get_already_generated_coins(const cryptonote::block& blk) const;
void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_weights, uint64_t already_generated_coins,
uint8_t hf_version = 1); uint8_t hf_version = 1);
bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list); std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list);
bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp); bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp);
bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc, bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc,
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>()); const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>());
@ -263,6 +263,30 @@ bool check_tx_verification_context(const cryptonote::tx_verification_context& tv
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
template<class t_test_class> template<class t_test_class>
auto do_check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_index, const std::vector<cryptonote::transaction>& txs, t_test_class& validator, int)
-> decltype(validator.check_tx_verification_context(tvcs, tx_added, event_index, txs))
{
return validator.check_tx_verification_context(tvcs, tx_added, event_index, txs);
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool do_check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t /*event_index*/, const std::vector<cryptonote::transaction>& /*txs*/, t_test_class&, long)
{
// Default block verification context check
for (const cryptonote::tx_verification_context &tvc: tvcs)
if (tvc.m_verifivation_failed)
throw std::runtime_error("Transaction verification failed");
return true;
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_index, const std::vector<cryptonote::transaction>& txs, t_test_class& validator)
{
// SFINAE in action
return do_check_tx_verification_context(tvcs, tx_added, event_index, txs, validator, 0);
}
//--------------------------------------------------------------------------
template<class t_test_class>
auto do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator, int) auto do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator, int)
-> decltype(validator.check_block_verification_context(bvc, event_index, blk)) -> decltype(validator.check_block_verification_context(bvc, event_index, blk))
{ {
@ -339,6 +363,26 @@ public:
return true; return true;
} }
bool operator()(const std::vector<cryptonote::transaction>& txs) const
{
log_event("cryptonote::transaction");
std::vector<cryptonote::blobdata> tx_blobs;
std::vector<cryptonote::tx_verification_context> tvcs;
cryptonote::tx_verification_context tvc0 = AUTO_VAL_INIT(tvc0);
for (const auto &tx: txs)
{
tx_blobs.push_back(t_serializable_object_to_blob(tx));
tvcs.push_back(tvc0);
}
size_t pool_size = m_c.get_pool_transactions_count();
m_c.handle_incoming_txs(tx_blobs, tvcs, m_txs_keeped_by_block, false, false);
size_t tx_added = m_c.get_pool_transactions_count() - pool_size;
bool r = check_tx_verification_context(tvcs, tx_added, m_ev_index, txs, m_validator);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
return true;
}
bool operator()(const cryptonote::block& b) const bool operator()(const cryptonote::block& b) const
{ {
log_event("cryptonote::block"); log_event("cryptonote::block");

View File

@ -224,6 +224,22 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_2_no_threshold); GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_2_no_threshold);
GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold); GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold);
GENERATE_AND_PLAY(gen_bp_tx_valid_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1);
GENERATE_AND_PLAY(gen_bp_tx_valid_2);
GENERATE_AND_PLAY(gen_bp_tx_valid_3);
GENERATE_AND_PLAY(gen_bp_tx_valid_16);
GENERATE_AND_PLAY(gen_bp_tx_invalid_4_2_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_16_16);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2);
GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_3_and_2_and_4);
GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_empty_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount);
GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type);
el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error);
MLOG(level, "\nREPORT:"); MLOG(level, "\nREPORT:");
MLOG(level, " Test run: " << tests_count); MLOG(level, " Test run: " << tests_count);

View File

@ -42,6 +42,7 @@
#include "v2_tests.h" #include "v2_tests.h"
#include "rct.h" #include "rct.h"
#include "multisig.h" #include "multisig.h"
#include "bulletproofs.h"
/************************************************************************/ /************************************************************************/
/* */ /* */
/************************************************************************/ /************************************************************************/

View File

@ -285,7 +285,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
#endif #endif
std::vector<crypto::secret_key> additional_tx_secret_keys; std::vector<crypto::secret_key> additional_tx_secret_keys;
auto sources_copy = sources; 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, false, 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, msoutp);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
#ifndef NO_MULTISIG #ifndef NO_MULTISIG

View File

@ -133,7 +133,7 @@ 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"); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::secret_key amount_key; crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, 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::RCTTypeSimpleBulletproof) if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeBulletproof)
rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
else else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));

Binary file not shown.

View File

@ -173,3 +173,18 @@ set_property(TARGET levin_fuzz_tests
PROPERTY PROPERTY
FOLDER "tests") FOLDER "tests")
add_executable(bulletproof_fuzz_tests bulletproof.cpp fuzzer.cpp)
target_link_libraries(bulletproof_fuzz_tests
PRIVATE
common
epee
${Boost_THREAD_LIBRARY}
${Boost_CHRONO_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET bulletproof_fuzz_tests
PROPERTY
FOLDER "tests")

View File

@ -0,0 +1,70 @@
// Copyright (c) 2017-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.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "fuzzer.h"
class BulletproofFuzzer: public Fuzzer
{
public:
virtual int run(const std::string &filename);
private:
};
int BulletproofFuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
std::stringstream ss;
ss << s;
binary_archive<false> ba(ss);
rct::Bulletproof proof = AUTO_VAL_INIT(proof);
bool r = ::serialization::serialize(ba, proof);
if(!r)
{
std::cout << "Error: failed to parse bulletproof from file " << filename << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
BulletproofFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

View File

@ -40,8 +40,13 @@ set(performance_tests_headers
generate_key_image.h generate_key_image.h
generate_key_image_helper.h generate_key_image_helper.h
generate_keypair.h generate_keypair.h
signature.h
is_out_to_acc.h is_out_to_acc.h
subaddress_expand.h subaddress_expand.h
range_proof.h
bulletproof.h
crypto_ops.h
multiexp.h
multi_tx_test_base.h multi_tx_test_base.h
performance_tests.h performance_tests.h
performance_utils.h performance_utils.h

View File

@ -0,0 +1,100 @@
// Copyright (c) 2014-2017, 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 "ringct/rctSigs.h"
#include "ringct/bulletproofs.h"
template<bool a_verify, size_t n_amounts>
class test_bulletproof
{
public:
static const size_t approx_loop_count = 100 / n_amounts;
static const size_t loop_count = (approx_loop_count >= 10 ? approx_loop_count : 10) / (a_verify ? 1 : 5);
static const bool verify = a_verify;
bool init()
{
proof = rct::bulletproof_PROVE(std::vector<uint64_t>(n_amounts, 749327532984), rct::skvGen(n_amounts));
return true;
}
bool test()
{
bool ret = true;
if (verify)
ret = rct::bulletproof_VERIFY(proof);
else
rct::bulletproof_PROVE(std::vector<uint64_t>(n_amounts, 749327532984), rct::skvGen(n_amounts));
return ret;
}
private:
rct::Bulletproof proof;
};
template<bool batch, size_t start, size_t repeat, size_t mul, size_t add, size_t N>
class test_aggregated_bulletproof
{
public:
static const size_t loop_count = 500 / (N * repeat);
bool init()
{
size_t o = start;
for (size_t n = 0; n < N; ++n)
{
//printf("adding %zu times %zu\n", repeat, o);
for (size_t i = 0; i < repeat; ++i)
proofs.push_back(rct::bulletproof_PROVE(std::vector<uint64_t>(o, 749327532984), rct::skvGen(o)));
o = o * mul + add;
}
return true;
}
bool test()
{
if (batch)
{
return rct::bulletproof_VERIFY(proofs);
}
else
{
for (const rct::Bulletproof &proof: proofs)
if (!rct::bulletproof_VERIFY(proof))
return false;
return true;
}
}
private:
std::vector<rct::Bulletproof> proofs;
};

View File

@ -40,14 +40,15 @@
#include "multi_tx_test_base.h" #include "multi_tx_test_base.h"
template<size_t a_ring_size, bool a_rct> template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean>
class test_check_tx_signature : private multi_tx_test_base<a_ring_size> 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"); static_assert(0 < a_ring_size, "ring_size must be greater than 0");
public: public:
static const size_t loop_count = a_rct ? 10 : a_ring_size < 100 ? 100 : 10; static const size_t loop_count = a_rct ? (a_ring_size <= 2 ? 50 : 10) : a_ring_size < 100 ? 100 : 10;
static const size_t ring_size = a_ring_size; static const size_t ring_size = a_ring_size;
static const size_t outputs = a_outputs;
static const bool rct = a_rct; static const bool rct = a_rct;
typedef multi_tx_test_base<a_ring_size> base_class; typedef multi_tx_test_base<a_ring_size> base_class;
@ -62,13 +63,15 @@ public:
m_alice.generate(); m_alice.generate();
std::vector<tx_destination_entry> destinations; std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address, false)); destinations.push_back(tx_destination_entry(this->m_source_amount - outputs + 1, m_alice.get_keys().m_account_address, false));
for (size_t n = 1; n < outputs; ++n)
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));
crypto::secret_key tx_key; crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; 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}; 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)) 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))
return false; return false;
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
@ -80,7 +83,7 @@ public:
{ {
if (rct) if (rct)
{ {
if (m_tx.rct_signatures.type == rct::RCTTypeFull || m_tx.rct_signatures.type == rct::RCTTypeFullBulletproof) if (m_tx.rct_signatures.type == rct::RCTTypeFull)
return rct::verRct(m_tx.rct_signatures); return rct::verRct(m_tx.rct_signatures);
else else
return rct::verRctSimple(m_tx.rct_signatures); return rct::verRctSimple(m_tx.rct_signatures);
@ -97,3 +100,74 @@ private:
cryptonote::transaction m_tx; cryptonote::transaction m_tx;
crypto::hash m_tx_prefix_hash; crypto::hash m_tx_prefix_hash;
}; };
template<size_t a_ring_size, size_t a_outputs, size_t a_num_txes, size_t extra_outs = 0>
class test_check_tx_signature_aggregated_bulletproofs : private multi_tx_test_base<a_ring_size>
{
static_assert(0 < a_ring_size, "ring_size must be greater than 0");
public:
static const size_t loop_count = a_ring_size <= 2 ? 50 : 10;
static const size_t ring_size = a_ring_size;
static const size_t outputs = a_outputs;
typedef multi_tx_test_base<a_ring_size> base_class;
bool init()
{
using namespace cryptonote;
if (!base_class::init())
return false;
m_alice.generate();
std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(this->m_source_amount - outputs + 1, m_alice.get_keys().m_account_address, false));
for (size_t n = 1; n < outputs; ++n)
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));
crypto::secret_key tx_key;
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};
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))
return false;
}
if (extra_outs)
{
destinations.clear();
destinations.push_back(tx_destination_entry(this->m_source_amount - extra_outs + 1, m_alice.get_keys().m_account_address, false));
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))
return false;
}
return true;
}
bool test()
{
std::vector<const rct::rctSig*> rvv;
rvv.reserve(m_txes.size());
for (size_t n = 0; n < m_txes.size(); ++n)
{
const rct::rctSig &rv = m_txes[n].rct_signatures;
if (!rct::verRctNonSemanticsSimple(rv))
return false;
rvv.push_back(&rv);
}
return rct::verRctSemanticsSimple(rvv);
}
private:
cryptonote::account_base m_alice;
std::vector<cryptonote::transaction> m_txes;
};

View File

@ -36,7 +36,7 @@
#include "multi_tx_test_base.h" #include "multi_tx_test_base.h"
template<size_t a_in_count, size_t a_out_count, bool a_rct> template<size_t a_in_count, size_t a_out_count, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean>
class test_construct_tx : private multi_tx_test_base<a_in_count> 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"); static_assert(0 < a_in_count, "in_count must be greater than 0");
@ -73,7 +73,7 @@ public:
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; 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}; 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); 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);
} }
private: private:

View File

@ -0,0 +1,122 @@
// Copyright (c) 2014-2017, 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 "crypto/crypto.h"
#include "ringct/rctOps.h"
enum test_op
{
op_sc_add,
op_sc_sub,
op_sc_mul,
op_ge_add_raw,
op_ge_add_p3_p3,
ops_fast,
op_addKeys,
op_scalarmultBase,
op_scalarmultKey,
op_scalarmultH,
op_scalarmult8,
op_ge_double_scalarmult_base_vartime,
op_ge_double_scalarmult_precomp_vartime,
op_ge_double_scalarmult_precomp_vartime2,
op_addKeys2,
op_addKeys3,
op_addKeys3_2,
op_isInMainSubgroup,
};
template<test_op op>
class test_crypto_ops
{
public:
static const size_t loop_count = op < ops_fast ? 10000000 : 1000;
bool init()
{
scalar0 = rct::skGen();
scalar1 = rct::skGen();
point0 = rct::scalarmultBase(rct::skGen());
point1 = rct::scalarmultBase(rct::skGen());
if (ge_frombytes_vartime(&p3_0, point0.bytes) != 0)
return false;
if (ge_frombytes_vartime(&p3_1, point1.bytes) != 0)
return false;
ge_p3_to_cached(&cached, &p3_0);
rct::precomp(precomp0, point0);
rct::precomp(precomp1, point1);
return true;
}
bool test()
{
rct::key key;
ge_cached tmp_cached;
ge_p1p1 tmp_p1p1;
ge_p2 tmp_p2;
switch (op)
{
case op_sc_add: sc_add(key.bytes, scalar0.bytes, scalar1.bytes); break;
case op_sc_sub: sc_sub(key.bytes, scalar0.bytes, scalar1.bytes); break;
case op_sc_mul: sc_mul(key.bytes, scalar0.bytes, scalar1.bytes); break;
case op_ge_add_p3_p3: {
ge_p3_to_cached(&tmp_cached, &p3_0);
ge_add(&tmp_p1p1, &p3_1, &tmp_cached);
ge_p1p1_to_p3(&p3_1, &tmp_p1p1);
break;
}
case op_ge_add_raw: ge_add(&tmp_p1p1, &p3_1, &cached); break;
case op_addKeys: rct::addKeys(key, point0, point1); break;
case op_scalarmultBase: rct::scalarmultBase(scalar0); break;
case op_scalarmultKey: rct::scalarmultKey(point0, scalar0); break;
case op_scalarmultH: rct::scalarmultH(scalar0); break;
case op_scalarmult8: rct::scalarmult8(point0); break;
case op_ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes); break;
case op_ge_double_scalarmult_precomp_vartime: ge_double_scalarmult_precomp_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes, precomp0); break;
case op_ge_double_scalarmult_precomp_vartime2: ge_double_scalarmult_precomp_vartime2(&tmp_p2, scalar0.bytes, precomp0, scalar1.bytes, precomp1); break;
case op_addKeys2: rct::addKeys2(key, scalar0, scalar1, point0); break;
case op_addKeys3: rct::addKeys3(key, scalar0, point0, scalar1, precomp1); break;
case op_addKeys3_2: rct::addKeys3(key, scalar0, precomp0, scalar1, precomp1); break;
case op_isInMainSubgroup: rct::isInMainSubgroup(point0); break;
default: return false;
}
return true;
}
private:
rct::key scalar0, scalar1;
rct::key point0, point1;
ge_p3 p3_0, p3_1;
ge_cached cached;
ge_dsmp precomp0, precomp1;
};

View File

@ -46,12 +46,18 @@
#include "generate_key_image.h" #include "generate_key_image.h"
#include "generate_key_image_helper.h" #include "generate_key_image_helper.h"
#include "generate_keypair.h" #include "generate_keypair.h"
#include "signature.h"
#include "is_out_to_acc.h" #include "is_out_to_acc.h"
#include "subaddress_expand.h" #include "subaddress_expand.h"
#include "sc_reduce32.h" #include "sc_reduce32.h"
#include "cn_fast_hash.h" #include "cn_fast_hash.h"
#include "rct_mlsag.h" #include "rct_mlsag.h"
#include "equality.h" #include "equality.h"
#include "range_proof.h"
#include "rct_mlsag.h"
#include "bulletproof.h"
#include "crypto_ops.h"
#include "multiexp.h"
namespace po = boost::program_options; namespace po = boost::program_options;
@ -63,11 +69,16 @@ int main(int argc, char** argv)
set_thread_high_priority(); set_thread_high_priority();
mlog_configure(mlog_get_default_log_path("performance_tests.log"), true); mlog_configure(mlog_get_default_log_path("performance_tests.log"), true);
mlog_set_log_level(0);
po::options_description desc_options("Command line options"); po::options_description desc_options("Command line options");
const command_line::arg_descriptor<std::string> arg_filter = { "filter", "Regular expression filter for which tests to run" }; const command_line::arg_descriptor<std::string> arg_filter = { "filter", "Regular expression filter for which tests to run" };
command_line::add_arg(desc_options, arg_filter); const command_line::arg_descriptor<bool> arg_verbose = { "verbose", "Verbose output", false };
const command_line::arg_descriptor<bool> arg_stats = { "stats", "Including statistics (min/median)", false };
const command_line::arg_descriptor<unsigned> arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 };
command_line::add_arg(desc_options, arg_filter, "");
command_line::add_arg(desc_options, arg_verbose, "");
command_line::add_arg(desc_options, arg_stats, "");
command_line::add_arg(desc_options, arg_loop_multiplier, "");
po::variables_map vm; po::variables_map vm;
bool r = command_line::handle_error_helper(desc_options, [&]() bool r = command_line::handle_error_helper(desc_options, [&]()
@ -80,82 +91,455 @@ int main(int argc, char** argv)
return 1; return 1;
const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter));
Params p;
p.verbose = command_line::get_arg(vm, arg_verbose);
p.stats = command_line::get_arg(vm, arg_stats);
p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier);
performance_timer timer; performance_timer timer;
timer.start(); timer.start();
TEST_PERFORMANCE3(filter, test_construct_tx, 1, 1, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 1, 2, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 2, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 1, 10, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 10, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 1, 100, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 100, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 1, 1000, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1000, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 1, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 2, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 10, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 100, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 100, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 1, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 2, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 10, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 100, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 100, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 1, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 2, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 10, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 100, false); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 100, false);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 1, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 2, 10, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 1, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 10, 10, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 1, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true);
TEST_PERFORMANCE3(filter, test_construct_tx, 100, 10, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 1, false); TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 2, false); TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 10, false); TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 100, false);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 2, true); TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 10, true); TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE2(filter, test_check_tx_signature, 100, true); TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE0(filter, test_is_out_to_acc); TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE0(filter, test_is_out_to_acc_precomp); TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE0(filter, test_generate_key_image_helper); TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE0(filter, test_generate_key_derivation);
TEST_PERFORMANCE0(filter, test_generate_key_image);
TEST_PERFORMANCE0(filter, test_derive_public_key);
TEST_PERFORMANCE0(filter, test_derive_secret_key);
TEST_PERFORMANCE0(filter, test_ge_frombytes_vartime);
TEST_PERFORMANCE0(filter, test_generate_keypair);
TEST_PERFORMANCE0(filter, test_sc_reduce32);
TEST_PERFORMANCE2(filter, test_wallet2_expand_subaddresses, 50, 200); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, false);
TEST_PERFORMANCE0(filter, test_cn_slow_hash); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE1(filter, test_cn_fast_hash, 32); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE1(filter, test_cn_fast_hash, 16384); 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_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE2(filter, test_equality, memcmp32, true); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64);
TEST_PERFORMANCE2(filter, test_equality, memcmp32, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64);
TEST_PERFORMANCE2(filter, test_equality, verify32, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64);
TEST_PERFORMANCE2(filter, test_equality, verify32, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16);
TEST_PERFORMANCE0(filter, p, test_is_out_to_acc);
TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp);
TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper);
TEST_PERFORMANCE0(filter, p, test_generate_key_derivation);
TEST_PERFORMANCE0(filter, p, test_generate_key_image);
TEST_PERFORMANCE0(filter, p, test_derive_public_key);
TEST_PERFORMANCE0(filter, p, test_derive_secret_key);
TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime);
TEST_PERFORMANCE0(filter, p, test_generate_keypair);
TEST_PERFORMANCE0(filter, p, test_sc_reduce32);
TEST_PERFORMANCE1(filter, p, test_signature, false);
TEST_PERFORMANCE1(filter, p, test_signature, true);
TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200);
TEST_PERFORMANCE0(filter, p, test_cn_slow_hash);
TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32);
TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, true);
TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, true);
TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, false);
TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);
TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);
TEST_PERFORMANCE1(filter, p, test_range_proof, true);
TEST_PERFORMANCE1(filter, p, test_range_proof, false);
TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount
TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1);
TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 2); // 1 bulletproof with 2 amounts
TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 2);
TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 15); // 1 bulletproof with 15 amounts
TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 15);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, false);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, true);
TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, true);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_add);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_sub);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_mul);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_raw);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_p3_p3);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultBase);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultH);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmult8);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys2);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_isInMainSubgroup);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 8);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 16);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 32);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 64);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 128);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 256);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 512);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 1024);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2048);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4096);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 8);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 16);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 32);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 64);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 128);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 256);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 512);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 1024);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2048);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4096);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 8);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 16);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 32);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 64);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 128);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 256);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 512);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 1024);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2048);
TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4096);
#if 1
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9);
#else
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 9);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 1);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 2);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 3);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 4);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 5);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 6);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 7);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 8);
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9);
#endif
std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl;

View File

@ -0,0 +1,94 @@
// 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <vector>
#include "ringct/rctOps.h"
#include "ringct/multiexp.h"
enum test_multiexp_algorithm
{
multiexp_bos_coster,
multiexp_straus,
multiexp_straus_cached,
multiexp_pippenger,
multiexp_pippenger_cached,
};
template<test_multiexp_algorithm algorithm, size_t npoints, size_t c=0>
class test_multiexp
{
public:
static const size_t loop_count = npoints >= 1024 ? 10 : npoints < 256 ? 1000 : 100;
bool init()
{
data.resize(npoints);
res = rct::identity();
for (size_t n = 0; n < npoints; ++n)
{
data[n].scalar = rct::skGen();
rct::key point = rct::scalarmultBase(rct::skGen());
if (ge_frombytes_vartime(&data[n].point, point.bytes))
return false;
rct::key kn = rct::scalarmultKey(point, data[n].scalar);
res = rct::addKeys(res, kn);
}
straus_cache = rct::straus_init_cache(data);
pippenger_cache = rct::pippenger_init_cache(data);
return true;
}
bool test()
{
switch (algorithm)
{
case multiexp_bos_coster:
return res == bos_coster_heap_conv_robust(data);
case multiexp_straus:
return res == straus(data);
case multiexp_straus_cached:
return res == straus(data, straus_cache);
case multiexp_pippenger:
return res == pippenger(data, NULL, c);
case multiexp_pippenger_cached:
return res == pippenger(data, pippenger_cache, c);
default:
return false;
}
}
private:
std::vector<rct::MultiexpData> data;
std::shared_ptr<rct::straus_cached_data> straus_cache;
std::shared_ptr<rct::pippenger_cached_data> pippenger_cache;
rct::key res;
};

View File

@ -36,6 +36,9 @@
#include <boost/chrono.hpp> #include <boost/chrono.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include "misc_language.h"
#include "common/perf_timer.h"
class performance_timer class performance_timer
{ {
public: public:
@ -62,13 +65,21 @@ private:
clock::time_point m_start; clock::time_point m_start;
}; };
struct Params
{
bool verbose;
bool stats;
unsigned loop_multiplier;
};
template <typename T> template <typename T>
class test_runner class test_runner
{ {
public: public:
test_runner() test_runner(const Params &params)
: m_elapsed(0) : m_elapsed(0)
, m_params(params)
, m_per_call_timers(T::loop_count * params.loop_multiplier, {true})
{ {
} }
@ -81,13 +92,18 @@ public:
performance_timer timer; performance_timer timer;
timer.start(); timer.start();
warm_up(); warm_up();
if (m_params.verbose)
std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl;
timer.start(); timer.start();
for (size_t i = 0; i < T::loop_count; ++i) for (size_t i = 0; i < T::loop_count * m_params.loop_multiplier; ++i)
{ {
if (m_params.stats)
m_per_call_timers[i].resume();
if (!test.test()) if (!test.test())
return false; return false;
if (m_params.stats)
m_per_call_timers[i].pause();
} }
m_elapsed = timer.elapsed_ms(); m_elapsed = timer.elapsed_ms();
@ -99,9 +115,62 @@ public:
int time_per_call(int scale = 1) const int time_per_call(int scale = 1) const
{ {
static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); static_assert(0 < T::loop_count, "T::loop_count must be greater than 0");
return m_elapsed * scale / T::loop_count; return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier);
} }
uint64_t per_call_min() const
{
uint64_t v = std::numeric_limits<uint64_t>::max();
for (const auto &pt: m_per_call_timers)
v = std::min(v, pt.value());
return v;
}
uint64_t per_call_max() const
{
uint64_t v = std::numeric_limits<uint64_t>::min();
for (const auto &pt: m_per_call_timers)
v = std::max(v, pt.value());
return v;
}
uint64_t per_call_mean() const
{
uint64_t v = 0;
for (const auto &pt: m_per_call_timers)
v += pt.value();
return v / m_per_call_timers.size();
}
uint64_t per_call_median() const
{
std::vector<uint64_t> values;
values.reserve(m_per_call_timers.size());
for (const auto &pt: m_per_call_timers)
values.push_back(pt.value());
return epee::misc_utils::median(values);
}
uint64_t per_call_stddev() const
{
if (m_per_call_timers.size() <= 1)
return 0;
const uint64_t mean = per_call_mean();
uint64_t acc = 0;
for (const auto &pt: m_per_call_timers)
{
int64_t dv = pt.value() - mean;
acc += dv * dv;
}
acc /= m_per_call_timers.size () - 1;
return sqrt(acc);
}
uint64_t min_time_ns() const { return tools::ticks_to_ns(per_call_min()); }
uint64_t max_time_ns() const { return tools::ticks_to_ns(per_call_max()); }
uint64_t median_time_ns() const { return tools::ticks_to_ns(per_call_median()); }
uint64_t standard_deviation_time_ns() const { return tools::ticks_to_ns(per_call_stddev()); }
private: private:
/** /**
* Warm up processor core, enabling turbo boost, etc. * Warm up processor core, enabling turbo boost, etc.
@ -120,22 +189,39 @@ private:
private: private:
volatile uint64_t m_warm_up; ///<! This field is intended for preclude compiler optimizations volatile uint64_t m_warm_up; ///<! This field is intended for preclude compiler optimizations
int m_elapsed; int m_elapsed;
Params m_params;
std::vector<tools::PerformanceTimer> m_per_call_timers;
}; };
template <typename T> template <typename T>
void run_test(const std::string &filter, const char* test_name) void run_test(const std::string &filter, const Params &params, const char* test_name)
{ {
boost::smatch match; boost::smatch match;
if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter)))
return; return;
test_runner<T> runner; test_runner<T> runner(params);
if (runner.run()) if (runner.run())
{
if (params.verbose)
{ {
std::cout << test_name << " - OK:\n"; std::cout << test_name << " - OK:\n";
std::cout << " loop count: " << T::loop_count << '\n'; std::cout << " loop count: " << T::loop_count * params.loop_multiplier << '\n';
std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; std::cout << " elapsed: " << runner.elapsed_time() << " ms\n";
if (params.stats)
{
std::cout << " min: " << runner.min_time_ns() << " ns\n";
std::cout << " max: " << runner.max_time_ns() << " ns\n";
std::cout << " median: " << runner.median_time_ns() << " ns\n";
std::cout << " std dev: " << runner.standard_deviation_time_ns() << " ns\n";
}
}
else
{
std::cout << test_name << " (" << T::loop_count * params.loop_multiplier << " calls) - OK:";
}
const char *unit = "ms"; const char *unit = "ms";
uint64_t scale = 1000000;
int time_per_call = runner.time_per_call(); int time_per_call = runner.time_per_call();
if (time_per_call < 30000) { if (time_per_call < 30000) {
time_per_call = runner.time_per_call(1000); time_per_call = runner.time_per_call(1000);
@ -144,8 +230,17 @@ void run_test(const std::string &filter, const char* test_name)
#else #else
unit = "µs"; unit = "µs";
#endif #endif
scale = 1000;
} }
std::cout << " time per call: " << time_per_call << " " << unit << "/call\n" << std::endl; std::cout << (params.verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (params.verbose ? "\n" : "");
if (params.stats)
{
uint64_t min_ns = runner.min_time_ns() / scale;
uint64_t med_ns = runner.median_time_ns() / scale;
uint64_t stddev_ns = runner.standard_deviation_time_ns() / scale;
std::cout << " (min " << min_ns << " " << unit << ", median " << med_ns << " " << unit << ", std dev " << stddev_ns << " " << unit << ")";
}
std::cout << std::endl;
} }
else else
{ {
@ -154,7 +249,10 @@ void run_test(const std::string &filter, const char* test_name)
} }
#define QUOTEME(x) #x #define QUOTEME(x) #x
#define TEST_PERFORMANCE0(filter, test_class) run_test< test_class >(filter, QUOTEME(test_class)) #define TEST_PERFORMANCE0(filter, params, test_class) run_test< test_class >(filter, params, QUOTEME(test_class))
#define TEST_PERFORMANCE1(filter, test_class, a0) run_test< test_class<a0> >(filter, QUOTEME(test_class<a0>)) #define TEST_PERFORMANCE1(filter, params, test_class, a0) run_test< test_class<a0> >(filter, params, QUOTEME(test_class<a0>))
#define TEST_PERFORMANCE2(filter, test_class, a0, a1) run_test< test_class<a0, a1> >(filter, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") #define TEST_PERFORMANCE2(filter, params, test_class, a0, a1) run_test< test_class<a0, a1> >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">")
#define TEST_PERFORMANCE3(filter, test_class, a0, a1, a2) run_test< test_class<a0, a1, a2> >(filter, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") #define TEST_PERFORMANCE3(filter, params, test_class, a0, a1, a2) run_test< test_class<a0, a1, a2> >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">")
#define TEST_PERFORMANCE4(filter, params, test_class, a0, a1, a2, a3) run_test< test_class<a0, a1, a2, a3> >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">")
#define TEST_PERFORMANCE5(filter, params, test_class, a0, a1, a2, a3, a4) run_test< test_class<a0, a1, a2, a3, a4> >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ">")
#define TEST_PERFORMANCE6(filter, params, test_class, a0, a1, a2, a3, a4, a5) run_test< test_class<a0, a1, a2, a3, a4, a5> >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ", " QUOTEME(a5) ">")

View File

@ -0,0 +1,63 @@
// Copyright (c) 2014-2017, 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 "ringct/rctSigs.h"
template<bool a_verify>
class test_range_proof
{
public:
static const size_t loop_count = 50;
static const bool verify = a_verify;
bool init()
{
rct::key mask;
sig = rct::proveRange(C, mask, 84932483243793);
return true;
}
bool test()
{
bool ret = true;
rct::key mask;
if (verify)
ret = rct::verRange(C, sig);
else
rct::proveRange(C, mask, 84932483243793);
return ret;
}
private:
rct::key C;
rct::rangeSig sig;
};

View File

@ -0,0 +1,68 @@
// 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 "crypto/crypto.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "single_tx_test_base.h"
template<bool verify>
class test_signature : public single_tx_test_base
{
public:
static const size_t loop_count = 10000;
bool init()
{
if (!single_tx_test_base::init())
return false;
message = crypto::rand<crypto::hash>();
keys = cryptonote::keypair::generate(hw::get_device("default"));
crypto::generate_signature(message, keys.pub, keys.sec, m_signature);
return true;
}
bool test()
{
if (verify)
return crypto::check_signature(message, keys.pub, m_signature);
crypto::generate_signature(message, keys.pub, keys.sec, m_signature);
return true;
}
private:
cryptonote::keypair keys;
crypto::hash message;
crypto::signature m_signature;
};

View File

@ -58,6 +58,7 @@ set(unit_tests_sources
mlocker.cpp mlocker.cpp
mnemonics.cpp mnemonics.cpp
mul_div.cpp mul_div.cpp
multiexp.cpp
multisig.cpp multisig.cpp
parse_amount.cpp parse_amount.cpp
random.cpp random.cpp
@ -78,7 +79,8 @@ set(unit_tests_sources
vercmp.cpp vercmp.cpp
ringdb.cpp ringdb.cpp
wipeable_string.cpp wipeable_string.cpp
is_hdd.cpp) is_hdd.cpp
aligned.cpp)
set(unit_tests_headers set(unit_tests_headers
unit_tests_utils.h) unit_tests_utils.h)

View File

@ -0,0 +1,86 @@
// 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.
#include "gtest/gtest.h"
#include "common/aligned.h"
TEST(aligned, large_null) { ASSERT_TRUE(aligned_malloc((size_t)-1, 1) == NULL); }
TEST(aligned, free_null) { aligned_free(NULL); }
TEST(aligned, zero) { void *ptr = aligned_malloc(0, 1); ASSERT_TRUE(ptr); aligned_free(ptr); }
TEST(aligned, aligned1) { void *ptr = aligned_malloc(1, 1); ASSERT_TRUE(ptr); aligned_free(ptr); }
TEST(aligned, aligned4096) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); }
TEST(aligned, aligned8) { void *ptr = aligned_malloc(1, 8); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 7) == 0); aligned_free(ptr); }
TEST(aligned, realloc_null) { void *ptr = aligned_realloc(NULL, 1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); }
TEST(aligned, realloc_diff_align) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(!aligned_realloc(ptr, 1, 2048)); aligned_free(ptr); }
TEST(aligned, realloc_same) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 1, 4096); ASSERT_TRUE(ptr == ptr2); aligned_free(ptr2); }
TEST(aligned, realloc_larger) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 2, 4096); ASSERT_TRUE(ptr != ptr2); aligned_free(ptr2); }
TEST(aligned, realloc_zero) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 0, 4096); ASSERT_TRUE(ptr && !ptr2); }
TEST(aligned, contents_larger)
{
unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256);
ASSERT_TRUE(ptr);
for (int n = 0; n < 50; ++n)
ptr[n] = n;
unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 51, 256);
for (int n = 0; n < 50; ++n)
{
ASSERT_TRUE(ptr2[n] == n);
}
aligned_free(ptr2);
}
TEST(aligned, contents_same)
{
unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256);
ASSERT_TRUE(ptr);
for (int n = 0; n < 50; ++n)
ptr[n] = n;
unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 50, 256);
for (int n = 0; n < 50; ++n)
{
ASSERT_TRUE(ptr2[n] == n);
}
aligned_free(ptr2);
}
TEST(aligned, contents_smaller)
{
unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256);
ASSERT_TRUE(ptr);
for (int n = 0; n < 50; ++n)
ptr[n] = n;
unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 49, 256);
for (int n = 0; n < 49; ++n)
{
ASSERT_TRUE(ptr2[n] == n);
}
aligned_free(ptr2);
}

View File

@ -40,14 +40,14 @@ namespace
class block_reward_and_already_generated_coins : public ::testing::Test class block_reward_and_already_generated_coins : public ::testing::Test
{ {
protected: protected:
static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; static const size_t current_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2;
bool m_block_not_too_big; bool m_block_not_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
}; };
#define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \
m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward,1); \ m_block_not_too_big = get_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,1); \
ASSERT_TRUE(m_block_not_too_big); \ ASSERT_TRUE(m_block_not_too_big); \
ASSERT_EQ(m_block_reward, expected_reward); ASSERT_EQ(m_block_reward, expected_reward);
@ -74,7 +74,7 @@ namespace
} }
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
class block_reward_and_current_block_size : public ::testing::Test class block_reward_and_current_block_weight : public ::testing::Test
{ {
protected: protected:
virtual void SetUp() virtual void SetUp()
@ -84,9 +84,9 @@ namespace
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
} }
void do_test(size_t median_block_size, size_t current_block_size) void do_test(size_t median_block_weight, size_t current_block_weight)
{ {
m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward, 1); m_block_not_too_big = get_block_reward(median_block_weight, current_block_weight, already_generated_coins, m_block_reward, 1);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
@ -96,28 +96,28 @@ namespace
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
@ -125,21 +125,21 @@ namespace
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1);
ASSERT_FALSE(m_block_not_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
#ifdef __x86_64__ // For 64-bit systems only, because block size is limited to size_t. #ifdef __x86_64__ // For 64-bit systems only, because block size is limited to size_t.
TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size) TEST_F(block_reward_and_current_block_weight, fails_on_huge_median_size)
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -147,7 +147,7 @@ namespace
#endif #endif
} }
TEST_F(block_reward_and_current_block_size, fails_on_huge_block_size) TEST_F(block_reward_and_current_block_weight, fails_on_huge_block_weight)
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -157,94 +157,94 @@ namespace
#endif // __x86_64__ #endif // __x86_64__
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
class block_reward_and_last_block_sizes : public ::testing::Test class block_reward_and_last_block_weights : public ::testing::Test
{ {
protected: protected:
virtual void SetUp() virtual void SetUp()
{ {
m_last_block_sizes.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; m_last_block_weights_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward, 1); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), 0, already_generated_coins, m_standard_block_reward, 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
} }
void do_test(size_t current_block_size) void do_test(size_t current_block_weight)
{ {
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward, 1); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), current_block_weight, already_generated_coins, m_block_reward, 1);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
std::vector<size_t> m_last_block_sizes; std::vector<size_t> m_last_block_weights;
uint64_t m_last_block_sizes_median; uint64_t m_last_block_weights_median;
bool m_block_not_too_big; bool m_block_not_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_median)
{ {
do_test(m_last_block_sizes_median - 1); do_test(m_last_block_weights_median - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_median)
{ {
do_test(m_last_block_sizes_median); do_test(m_last_block_weights_median);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_median)
{ {
do_test(m_last_block_sizes_median + 1); do_test(m_last_block_weights_median + 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_2_medians)
{ {
do_test(2 * m_last_block_sizes_median - 1); do_test(2 * m_last_block_weights_median - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_2_medians)
{ {
do_test(2 * m_last_block_sizes_median); do_test(2 * m_last_block_weights_median);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_2_medians)
{ {
do_test(2 * m_last_block_sizes_median + 1); do_test(2 * m_last_block_weights_median + 1);
ASSERT_FALSE(m_block_not_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
TEST_F(block_reward_and_last_block_sizes, calculates_correctly) TEST_F(block_reward_and_last_block_weights, calculates_correctly)
{ {
ASSERT_EQ(0, m_last_block_sizes_median % 8); ASSERT_EQ(0, m_last_block_weights_median % 8);
do_test(m_last_block_sizes_median * 9 / 8); do_test(m_last_block_weights_median * 9 / 8);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64);
// 3/2 = 12/8 // 3/2 = 12/8
do_test(m_last_block_sizes_median * 3 / 2); do_test(m_last_block_weights_median * 3 / 2);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4); ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4);
do_test(m_last_block_sizes_median * 15 / 8); do_test(m_last_block_weights_median * 15 / 8);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64);
} }

View File

@ -319,7 +319,7 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData)
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_diffs[0], t_coins[0], this->m_txs[0]));
ASSERT_EQ(t_sizes[0], this->m_db->get_block_size(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_cumulative_difficulty(0));
ASSERT_EQ(t_diffs[0], this->m_db->get_block_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_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));

View File

@ -30,8 +30,12 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "string_tools.h"
#include "ringct/rctOps.h" #include "ringct/rctOps.h"
#include "ringct/rctSigs.h"
#include "ringct/bulletproofs.h" #include "ringct/bulletproofs.h"
#include "device/device.hpp"
#include "misc_log_ex.h"
TEST(bulletproofs, valid_zero) TEST(bulletproofs, valid_zero)
{ {
@ -54,6 +58,108 @@ TEST(bulletproofs, valid_random)
} }
} }
TEST(bulletproofs, valid_multi_random)
{
for (int n = 0; n < 8; ++n)
{
size_t outputs = 2 + n;
std::vector<uint64_t> amounts;
rct::keyV gamma;
for (size_t i = 0; i < outputs; ++i)
{
amounts.push_back(crypto::rand<uint64_t>());
gamma.push_back(rct::skGen());
}
rct::Bulletproof proof = bulletproof_PROVE(amounts, gamma);
ASSERT_TRUE(rct::bulletproof_VERIFY(proof));
}
}
TEST(bulletproofs, multi_splitting)
{
rct::ctkeyV sc, pc;
rct::ctkey sctmp, pctmp;
std::vector<unsigned int> index;
std::vector<uint64_t> inamounts, outamounts;
std::tie(sctmp, pctmp) = rct::ctskpkGen(6000);
sc.push_back(sctmp);
pc.push_back(pctmp);
inamounts.push_back(6000);
index.push_back(1);
std::tie(sctmp, pctmp) = rct::ctskpkGen(7000);
sc.push_back(sctmp);
pc.push_back(pctmp);
inamounts.push_back(7000);
index.push_back(1);
const int mixin = 3, max_outputs = 16;
for (int n_outputs = 1; n_outputs <= max_outputs; ++n_outputs)
{
std::vector<uint64_t> outamounts;
rct::keyV amount_keys;
rct::keyV destinations;
rct::key Sk, Pk;
uint64_t available = 6000 + 7000;
uint64_t amount;
rct::ctkeyM mixRing(sc.size());
//add output
for (size_t i = 0; i < n_outputs; ++i)
{
amount = rct::randXmrAmount(available);
outamounts.push_back(amount);
amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
rct::skpkGen(Sk, Pk);
destinations.push_back(Pk);
available -= amount;
}
for (size_t i = 0; i < sc.size(); ++i)
{
for (size_t j = 0; j <= mixin; ++j)
{
if (j == 1)
mixRing[i].push_back(pc[i]);
else
mixRing[i].push_back({rct::scalarmultBase(rct::skGen()), rct::scalarmultBase(rct::skGen())});
}
}
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"));
ASSERT_TRUE(rct::verRctSimple(s));
for (size_t i = 0; i < n_outputs; ++i)
{
rct::key mask;
rct::decodeRctSimple(s, amount_keys[i], i, mask, hw::get_device("default"));
ASSERT_TRUE(mask == outSk[i].mask);
}
}
}
TEST(bulletproofs, valid_aggregated)
{
static const size_t N_PROOFS = 8;
std::vector<rct::Bulletproof> proofs(N_PROOFS);
for (size_t n = 0; n < N_PROOFS; ++n)
{
size_t outputs = 2 + n;
std::vector<uint64_t> amounts;
rct::keyV gamma;
for (size_t i = 0; i < outputs; ++i)
{
amounts.push_back(crypto::rand<uint64_t>());
gamma.push_back(rct::skGen());
}
proofs[n] = bulletproof_PROVE(amounts, gamma);
}
ASSERT_TRUE(rct::bulletproof_VERIFY(proofs));
}
TEST(bulletproofs, invalid_8) TEST(bulletproofs, invalid_8)
{ {
rct::key invalid_amount = rct::zero(); rct::key invalid_amount = rct::zero();
@ -69,3 +175,72 @@ TEST(bulletproofs, invalid_31)
rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, rct::skGen()); rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, rct::skGen());
ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
} }
TEST(bulletproofs, invalid_gamma_0)
{
rct::key invalid_amount = rct::zero();
invalid_amount[8] = 1;
rct::key gamma = rct::zero();
rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
}
static const char * const torsion_elements[] =
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"0000000000000000000000000000000000000000000000000000000000000000",
"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
"0000000000000000000000000000000000000000000000000000000000000080",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
};
TEST(bulletproofs, invalid_torsion)
{
rct::Bulletproof proof = bulletproof_PROVE(7329838943733, rct::skGen());
ASSERT_TRUE(rct::bulletproof_VERIFY(proof));
for (const auto &xs: torsion_elements)
{
rct::key x;
ASSERT_TRUE(epee::string_tools::hex_to_pod(xs, x));
ASSERT_FALSE(rct::isInMainSubgroup(x));
for (auto &k: proof.V)
{
const rct::key org_k = k;
rct::addKeys(k, org_k, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
k = org_k;
}
for (auto &k: proof.L)
{
const rct::key org_k = k;
rct::addKeys(k, org_k, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
k = org_k;
}
for (auto &k: proof.R)
{
const rct::key org_k = k;
rct::addKeys(k, org_k, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
k = org_k;
}
const rct::key org_A = proof.A;
rct::addKeys(proof.A, org_A, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
proof.A = org_A;
const rct::key org_S = proof.S;
rct::addKeys(proof.S, org_S, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
proof.S = org_S;
const rct::key org_T1 = proof.T1;
rct::addKeys(proof.T1, org_T1, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
proof.T1 = org_T1;
const rct::key org_T2 = proof.T2;
rct::addKeys(proof.T2, org_T2, x);
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
proof.T2 = org_T2;
}
}

View File

@ -58,46 +58,46 @@ namespace
TEST_F(fee, 10xmr) TEST_F(fee, 10xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1, 3), 2000000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, 1, 3), 2000000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000));
} }
TEST_F(fee, 1xmr) TEST_F(fee, 1xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1, 3), 200000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, 1, 3), 200000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000));
} }
TEST_F(fee, dot3xmr) TEST_F(fee, dot3xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1, 3), 60000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, 1, 3), 60000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000));
} }
static bool is_more_or_less(double x, double y) static bool is_more_or_less(double x, double y)
@ -116,7 +116,7 @@ namespace
600000000000ull, // .6 monero, minimum reward per block at 2min 600000000000ull, // .6 monero, minimum reward per block at 2min
300000000000ull, // .3 monero, minimum reward per block at 1min 300000000000ull, // .3 monero, minimum reward per block at 1min
}; };
static const uint64_t median_block_sizes[] = { static const uint64_t median_block_weights[] = {
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2,
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2,
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10,
@ -127,9 +127,9 @@ namespace
for (uint64_t block_reward: block_rewards) for (uint64_t block_reward: block_rewards)
{ {
for (uint64_t median_block_size: median_block_sizes) for (uint64_t median_block_weight: median_block_weights)
{ {
ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_per_kb_fee(block_reward, median_block_size, 3) * (median_block_size / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_base_fee(block_reward, median_block_weight, 3) * (median_block_weight / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024));
} }
} }
} }

View File

@ -72,7 +72,7 @@ public:
virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } 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 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 uint64_t get_top_block_timestamp() const { return 0; }
virtual size_t get_block_size(const uint64_t& height) const { return 128; } virtual size_t get_block_weight(const uint64_t& height) const { return 128; }
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; }
virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } virtual 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_already_generated_coins(const uint64_t& height) const { return 10000000000; }
@ -130,7 +130,7 @@ public:
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; }
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs

View File

@ -75,7 +75,7 @@ namespace
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[from.m_account_address.m_spend_public_key] = {0,0}; 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)) 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))
throw std::runtime_error{"transaction construction error"}; throw std::runtime_error{"transaction construction error"};
return tx; return tx;

View File

@ -0,0 +1,254 @@
// 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.
#include "gtest/gtest.h"
#include "crypto/crypto.h"
#include "ringct/rctOps.h"
#include "ringct/multiexp.h"
static const rct::key TESTSCALAR = rct::skGen();
static const rct::key TESTPOW2SCALAR = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
static const rct::key TESTSMALLSCALAR = {{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
static const rct::key TESTPOINT = rct::scalarmultBase(rct::skGen());
static rct::key basic(const std::vector<rct::MultiexpData> &data)
{
ge_p3 res_p3 = ge_p3_identity;
for (const auto &d: data)
{
ge_cached cached;
ge_p3 p3;
ge_p1p1 p1;
ge_scalarmult_p3(&p3, d.scalar.bytes, &d.point);
ge_p3_to_cached(&cached, &p3);
ge_add(&p1, &res_p3, &cached);
ge_p1p1_to_p3(&res_p3, &p1);
}
rct::key res;
ge_p3_tobytes(res.bytes, &res_p3);
return res;
}
static ge_p3 get_p3(const rct::key &point)
{
ge_p3 p3;
EXPECT_TRUE(ge_frombytes_vartime(&p3, point.bytes) == 0);
return p3;
}
TEST(multiexp, bos_coster_empty)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
TEST(multiexp, straus_empty)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == straus(data));
}
TEST(multiexp, pippenger_empty)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == pippenger(data));
}
TEST(multiexp, bos_coster_zero_and_non_zero)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(TESTPOINT)});
data.push_back({TESTSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
TEST(multiexp, straus_zero_and_non_zero)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(TESTPOINT)});
data.push_back({TESTSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == straus(data));
}
TEST(multiexp, pippenger_zero_and_non_zero)
{
std::vector<rct::MultiexpData> data;
data.push_back({rct::zero(), get_p3(TESTPOINT)});
data.push_back({TESTSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == pippenger(data));
}
TEST(multiexp, bos_coster_pow2_scalar)
{
std::vector<rct::MultiexpData> data;
data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)});
data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
TEST(multiexp, straus_pow2_scalar)
{
std::vector<rct::MultiexpData> data;
data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)});
data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == straus(data));
}
TEST(multiexp, pippenger_pow2_scalar)
{
std::vector<rct::MultiexpData> data;
data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)});
data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == pippenger(data));
}
TEST(multiexp, bos_coster_only_zeroes)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({rct::zero(), get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
TEST(multiexp, straus_only_zeroes)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({rct::zero(), get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == straus(data));
}
TEST(multiexp, pippenger_only_zeroes)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({rct::zero(), get_p3(TESTPOINT)});
ASSERT_TRUE(basic(data) == pippenger(data));
}
TEST(multiexp, bos_coster_only_identities)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({TESTSCALAR, get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
TEST(multiexp, straus_only_identities)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({TESTSCALAR, get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == straus(data));
}
TEST(multiexp, pippenger_only_identities)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 16; ++n)
data.push_back({TESTSCALAR, get_p3(rct::identity())});
ASSERT_TRUE(basic(data) == pippenger(data));
}
TEST(multiexp, bos_coster_random)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 32; ++n)
{
data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))});
ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
}
}
TEST(multiexp, straus_random)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 32; ++n)
{
data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))});
ASSERT_TRUE(basic(data) == straus(data));
}
}
TEST(multiexp, pippenger_random)
{
std::vector<rct::MultiexpData> data;
for (int n = 0; n < 32; ++n)
{
data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))});
ASSERT_TRUE(basic(data) == pippenger(data));
}
}
TEST(multiexp, straus_cached)
{
static constexpr size_t N = 256;
std::vector<rct::MultiexpData> P(N);
for (size_t n = 0; n < N; ++n)
{
P[n].scalar = rct::zero();
ASSERT_TRUE(ge_frombytes_vartime(&P[n].point, rct::scalarmultBase(rct::skGen()).bytes) == 0);
}
std::shared_ptr<rct::straus_cached_data> cache = rct::straus_init_cache(P);
for (size_t n = 0; n < N/16; ++n)
{
std::vector<rct::MultiexpData> data;
size_t sz = 1 + crypto::rand<size_t>() % (N-1);
for (size_t s = 0; s < sz; ++s)
{
data.push_back({rct::skGen(), P[s].point});
}
ASSERT_TRUE(basic(data) == straus(data, cache));
}
}
TEST(multiexp, pippenger_cached)
{
static constexpr size_t N = 256;
std::vector<rct::MultiexpData> P(N);
for (size_t n = 0; n < N; ++n)
{
P[n].scalar = rct::zero();
ASSERT_TRUE(ge_frombytes_vartime(&P[n].point, rct::scalarmultBase(rct::skGen()).bytes) == 0);
}
std::shared_ptr<rct::pippenger_cached_data> cache = rct::pippenger_init_cache(P);
for (size_t n = 0; n < N/16; ++n)
{
std::vector<rct::MultiexpData> data;
size_t sz = 1 + crypto::rand<size_t>() % (N-1);
for (size_t s = 0; s < sz; ++s)
{
data.push_back({rct::skGen(), P[s].point});
}
ASSERT_TRUE(basic(data) == pippenger(data, cache));
}
}

View File

@ -1085,3 +1085,34 @@ TEST(ringct, zeroCommmit)
const rct::key manual = rct::addKeys(a, b); const rct::key manual = rct::addKeys(a, b);
ASSERT_EQ(z, manual); ASSERT_EQ(z, manual);
} }
TEST(ringct, H)
{
ge_p3 p3;
ASSERT_EQ(ge_frombytes_vartime(&p3, rct::H.bytes), 0);
ASSERT_EQ(memcmp(&p3, &ge_p3_H, sizeof(ge_p3)), 0);
}
TEST(ringct, mul8)
{
ASSERT_EQ(rct::scalarmult8(rct::identity()), rct::identity());
ASSERT_EQ(rct::scalarmult8(rct::H), rct::scalarmultKey(rct::H, rct::EIGHT));
ASSERT_EQ(rct::scalarmultKey(rct::scalarmultKey(rct::H, rct::INV_EIGHT), rct::EIGHT), rct::H);
}
TEST(ringct, aggregated)
{
static const size_t N_PROOFS = 16;
std::vector<rctSig> s(N_PROOFS);
std::vector<const rctSig*> sp(N_PROOFS);
for (size_t n = 0; n < N_PROOFS; ++n)
{
static const uint64_t inputs[] = {1000, 1000};
static const uint64_t outputs[] = {500, 1500};
s[n] = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 0);
sp[n] = &s[n];
}
ASSERT_TRUE(verRctSemanticsSimple(sp));
}