diff options
-rw-r--r-- | src/blockchain_db/blockchain_db.h | 3 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.cpp | 36 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.h | 3 | ||||
-rw-r--r-- | src/cryptonote_config.h | 2 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 4 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 3 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 2 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 2 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 6 | ||||
-rw-r--r-- | src/ringct/rctOps.cpp | 31 | ||||
-rw-r--r-- | src/ringct/rctOps.h | 4 | ||||
-rw-r--r-- | src/ringct/rctSigs.cpp | 142 | ||||
-rw-r--r-- | src/ringct/rctSigs.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 8 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 13 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 122 | ||||
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 48 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 1 | ||||
-rw-r--r-- | tests/unit_tests/hardfork.cpp | 2 |
20 files changed, 300 insertions, 136 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 5b6a793d8..91c388de6 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1309,10 +1309,11 @@ public: * * @param amounts optional set of amounts to lookup * @param unlocked whether to restrict count to unlocked outputs + * @param recent_cutoff timestamp to determine whether an output is recent * * @return a set of amount/instances */ - virtual std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const = 0; + virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const = 0; /** * @brief is BlockchainDB in read-only mode? diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index acb7d2cf6..b5459b56b 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2657,7 +2657,7 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std:: LOG_PRINT_L3("db3: " << db3); } -std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const +std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -2665,7 +2665,7 @@ std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vec TXN_PREFIX_RDONLY(); RCURSOR(output_amounts); - std::map<uint64_t, uint64_t> histogram; + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram; MDB_val k; MDB_val v; @@ -2683,7 +2683,7 @@ std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vec mdb_size_t num_elems = 0; mdb_cursor_count(m_cur_output_amounts, &num_elems); uint64_t amount = *(const uint64_t*)k.mv_data; - histogram[amount] = num_elems; + histogram[amount] = std::make_tuple(num_elems, 0, 0); } } else @@ -2694,13 +2694,13 @@ std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vec int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); if (ret == MDB_NOTFOUND) { - histogram[amount] = 0; + histogram[amount] = std::make_tuple(0, 0, 0); } else if (ret == MDB_SUCCESS) { mdb_size_t num_elems = 0; mdb_cursor_count(m_cur_output_amounts, &num_elems); - histogram[amount] = num_elems; + histogram[amount] = std::make_tuple(num_elems, 0, 0); } else { @@ -2709,11 +2709,11 @@ std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vec } } - if (unlocked) { + if (unlocked || recent_cutoff > 0) { const uint64_t blockchain_height = height(); - for (auto i: histogram) { - uint64_t amount = i.first; - uint64_t num_elems = i.second; + for (std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>::iterator i = histogram.begin(); i != histogram.end(); ++i) { + uint64_t amount = i->first; + uint64_t num_elems = std::get<0>(i->second); while (num_elems > 0) { const tx_out_index toi = get_output_tx_and_index(amount, num_elems - 1); const uint64_t height = get_tx_block_height(toi.first); @@ -2722,7 +2722,23 @@ std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vec --num_elems; } // modifying second does not invalidate the iterator - i.second = num_elems; + std::get<1>(i->second) = num_elems; + + if (recent_cutoff > 0) + { + uint64_t recent = 0; + while (num_elems > 0) { + const tx_out_index toi = get_output_tx_and_index(amount, num_elems - 1); + const uint64_t height = get_tx_block_height(toi.first); + const uint64_t ts = get_block_timestamp(height); + if (ts < recent_cutoff) + break; + --num_elems; + ++recent; + } + // modifying second does not invalidate the iterator + std::get<2>(i->second) = recent; + } } } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 9df4b86d1..6db5abca1 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -265,10 +265,11 @@ public: * * @param amounts optional set of amounts to lookup * @param unlocked whether to restrict count to unlocked outputs + * @param recent_cutoff timestamp to determine which outputs are recent * * @return a set of amount/instances */ - std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const; + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const; private: void do_resize(uint64_t size_increase=0); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 319e18808..66084da3c 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -127,7 +127,7 @@ namespace config { uint64_t const DEFAULT_FEE_ATOMIC_XMR_PER_KB = 500; // Just a placeholder! Change me! uint8_t const FEE_CALCULATION_MAX_RETRIES = 10; - uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)10000000000); // pow(10, 10) + uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9) uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8) std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9d2bf9332..9ea023a4c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3757,9 +3757,9 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting); } -std::map<uint64_t, uint64_t> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const +std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const { - return m_db->get_output_histogram(amounts, unlocked); + return m_db->get_output_histogram(amounts, unlocked, recent_cutoff); } #if defined(PER_BLOCK_CHECKPOINT) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 94701608e..262c2952b 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -729,10 +729,11 @@ namespace cryptonote * * @param amounts optional set of amounts to lookup * @param unlocked whether to restrict instances to unlocked ones + * @param recent_cutoff timestamp to consider outputs as recent * * @return a set of amount/instances */ - std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const; + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const; /** * @brief perform a check on all key images in the blockchain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 9a44d9d3f..a82d96416 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -616,7 +616,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const uint64_t count) + std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count) { std::list<block> blocks; std::list<transaction> txs; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 407e89197..d925a184d 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -605,7 +605,7 @@ namespace cryptonote * * @return the number of blocks to sync in one go */ - std::pair<uint64_t, uint64_t> get_coinbase_tx_sum(const uint64_t start_offset, const uint64_t count); + std::pair<uint64_t, uint64_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); private: diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index db0d0a6e4..bed10715b 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1243,6 +1243,8 @@ bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_c req.min_count = min_count; req.max_count = max_count; + req.unlocked = false; + req.recent_cutoff = 0; if (m_is_rpc) { @@ -1261,10 +1263,10 @@ bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_c } std::sort(res.histogram.begin(), res.histogram.end(), - [](const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e1, const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e2)->bool { return e1.instances < e2.instances; }); + [](const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e1, const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e2)->bool { return e1.total_instances < e2.total_instances; }); for (const auto &e: res.histogram) { - tools::msg_writer() << e.instances << " " << cryptonote::print_money(e.amount); + tools::msg_writer() << e.total_instances << " " << cryptonote::print_money(e.amount); } return true; diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 488e47ca0..239168388 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -320,7 +320,7 @@ namespace rct { //be careful these are also in crypto namespace //cn_fast_hash for arbitrary multiples of 32 bytes void cn_fast_hash(key &hash, const void * data, const std::size_t l) { - keccak((uint8_t *)data, l, hash.bytes, 32); + keccak((const uint8_t *)data, l, hash.bytes, 32); } void hash_to_scalar(key &hash, const void * data, const std::size_t l) { @@ -330,7 +330,7 @@ namespace rct { //cn_fast_hash for a 32 byte key void cn_fast_hash(key & hash, const key & in) { - keccak((uint8_t *)in.bytes, 32, hash.bytes, 32); + keccak((const uint8_t *)in.bytes, 32, hash.bytes, 32); } void hash_to_scalar(key & hash, const key & in) { @@ -341,7 +341,7 @@ namespace rct { //cn_fast_hash for a 32 byte key key cn_fast_hash(const key & in) { key hash; - keccak((uint8_t *)in.bytes, 32, hash.bytes, 32); + keccak((const uint8_t *)in.bytes, 32, hash.bytes, 32); return hash; } @@ -354,7 +354,7 @@ namespace rct { //cn_fast_hash for a 128 byte unsigned char key cn_fast_hash128(const void * in) { key hash; - keccak((uint8_t *)in, 128, hash.bytes, 32); + keccak((const uint8_t *)in, 128, hash.bytes, 32); return hash; } @@ -367,20 +367,13 @@ namespace rct { //cn_fast_hash for multisig purpose //This takes the outputs and commitments //and hashes them into a 32 byte sized key - key cn_fast_hash(ctkeyV PC) { - key rv = identity(); - std::size_t l = (std::size_t)PC.size(); - size_t i = 0, j = 0; - vector<char> m(l * 64); - for (i = 0 ; i < l ; i++) { - memcpy(&m[i * 64], &PC[i].dest, 32); - memcpy(&m[i * 64 + 32], &PC[i].mask, 32); - } - cn_fast_hash(rv, &m[0], 64*l); + key cn_fast_hash(const ctkeyV &PC) { + key rv; + cn_fast_hash(rv, &PC[0], 64*PC.size()); return rv; } - key hash_to_scalar(ctkeyV PC) { + key hash_to_scalar(const ctkeyV &PC) { key rv = cn_fast_hash(PC); sc_reduce32(rv.bytes); return rv; @@ -391,14 +384,8 @@ namespace rct { //put them in the key vector and it concatenates them //and then hashes them key cn_fast_hash(const keyV &keys) { - size_t l = keys.size(); - vector<unsigned char> m(l * 32); - size_t i; - for (i = 0 ; i < l ; i++) { - memcpy(&m[i * 32], keys[i].bytes, 32); - } key rv; - cn_fast_hash(rv, &m[0], 32 * l); + cn_fast_hash(rv, &keys[0], keys.size() * sizeof(keys[0])); //dp(rv); return rv; } diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 1e71c645d..a7e13eefa 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -149,8 +149,8 @@ namespace rct { //for mg sigs key cn_fast_hash128(const void * in); key hash_to_scalar128(const void * in); - key cn_fast_hash(ctkeyV PC); - key hash_to_scalar(ctkeyV PC); + key cn_fast_hash(const ctkeyV &PC); + key hash_to_scalar(const ctkeyV &PC); //for mg sigs key cn_fast_hash(const keyV &keys); key hash_to_scalar(const keyV &keys); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 80ece9c44..f7ea3729d 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -28,14 +28,26 @@ // 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 <boost/asio.hpp> #include "misc_log_ex.h" #include "common/perf_timer.h" +#include "common/util.h" #include "rctSigs.h" #include "cryptonote_core/cryptonote_format_utils.h" using namespace crypto; using namespace std; +#define KILL_IOSERVICE() \ + if(ioservice_active) \ + { \ + work.reset(); \ + while (!ioservice.stopped()) ioservice.poll(); \ + threadpool.join_all(); \ + ioservice.stop(); \ + ioservice_active = false; \ + } + namespace rct { //Schnorr Non-linkable @@ -43,7 +55,7 @@ namespace rct { //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 //These are called in the below ASNL sig generation - void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index) { + void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, unsigned int index) { key c1, c2, L2; key a = skGen(); if (index == 0) { @@ -95,7 +107,7 @@ namespace rct { asnlSig rv; rv.s = zero(); for (j = 0; j < ATOMS; j++) { - GenSchnorrNonLinkable(rv.L1[j], s1[j], rv.s2[j], x[j], P1[j], P2[j], (int)indices[j]); + GenSchnorrNonLinkable(rv.L1[j], s1[j], rv.s2[j], x[j], P1[j], P2[j], indices[j]); sc_add(rv.s.bytes, rv.s.bytes, s1[j].bytes); } return rv; @@ -348,9 +360,14 @@ namespace rct { return true; } + void verRangeWrapper(const key & C, const rangeSig & as, bool &result) { + result = verRange(C, as); + } + key get_pre_mlsag_hash(const rctSig &rv) { keyV hashes; + hashes.reserve(3); hashes.push_back(rv.message); crypto::hash h; @@ -364,6 +381,7 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; + kv.reserve((64*3+1) * rv.p.rangeSigs.size()); for (auto r: rv.p.rangeSigs) { for (size_t n = 0; n < 64; ++n) @@ -526,6 +544,10 @@ namespace rct { return MLSAG_Ver(message, M, mg, rows); } + void verRctMGSimpleWrapper(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C, bool &result) { + result = verRctMGSimple(message, mg, pubs, C); + } + //These functions get keys from blockchain //replace these when connecting blockchain //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with @@ -743,17 +765,41 @@ namespace rct { // some rct ops can throw try { - size_t i = 0; - bool tmp; + boost::asio::io_service ioservice; + boost::thread_group threadpool; + std::unique_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(ioservice)); + size_t threads = tools::get_max_concurrency(); + threads = std::min(threads, rv.outPk.size()); + for (size_t i = 0; i < threads; ++i) + threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice)); + bool ioservice_active = threads > 1; + std::deque<bool> results(rv.outPk.size(), false); + epee::misc_utils::auto_scope_leave_caller ioservice_killer = epee::misc_utils::create_scope_leave_handler([&]() { KILL_IOSERVICE(); }); + DP("range proofs verified?"); - for (i = 0; i < rv.outPk.size(); i++) { - tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - DP(tmp); - if (!tmp) { - LOG_ERROR("Range proof verification failed for input " << i); + for (size_t i = 0; i < rv.outPk.size(); i++) { + if (threads > 1) { + ioservice.dispatch(boost::bind(&verRangeWrapper, std::cref(rv.outPk[i].mask), std::cref(rv.p.rangeSigs[i]), std::ref(results[i]))); + } + else { + bool tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + DP(tmp); + if (!tmp) { + LOG_ERROR("Range proof verification failed for input " << i); + return false; + } + } + } + KILL_IOSERVICE(); + if (threads > 1) { + for (size_t i = 0; i < rv.outPk.size(); ++i) { + if (!results[i]) { + LOG_ERROR("Range proof verified failed for input " << i); return false; } + } } + //compute txn fee key txnFeeKey = scalarmultH(d2h(rv.txnFee)); bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); @@ -784,29 +830,87 @@ namespace rct { CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); - key sumOutpks = identity(); - for (i = 0; i < rv.outPk.size(); i++) { - if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) { + { + boost::asio::io_service ioservice; + boost::thread_group threadpool; + std::unique_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(ioservice)); + size_t threads = tools::get_max_concurrency(); + threads = std::min(threads, rv.outPk.size()); + for (size_t i = 0; i < threads; ++i) + threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice)); + bool ioservice_active = threads > 1; + std::deque<bool> results(rv.outPk.size(), false); + epee::misc_utils::auto_scope_leave_caller ioservice_killer = epee::misc_utils::create_scope_leave_handler([&]() { KILL_IOSERVICE(); }); + + for (i = 0; i < rv.outPk.size(); i++) { + if (threads > 1) { + ioservice.dispatch(boost::bind(&verRangeWrapper, std::cref(rv.outPk[i].mask), std::cref(rv.p.rangeSigs[i]), std::ref(results[i]))); + } + else if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) { LOG_ERROR("Range proof verified failed for input " << i); return false; } + } + KILL_IOSERVICE(); + if (threads > 1) { + for (size_t i = 0; i < rv.outPk.size(); ++i) { + if (!results[i]) { + LOG_ERROR("Range proof verified failed for input " << i); + return false; + } + } + } + } + + key sumOutpks = identity(); + for (i = 0; i < rv.outPk.size(); i++) { addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); } DP(sumOutpks); key txnFeeKey = scalarmultH(d2h(rv.txnFee)); addKeys(sumOutpks, txnFeeKey, sumOutpks); - bool tmpb = false; key message = get_pre_mlsag_hash(rv); - key sumPseudoOuts = identity(); - for (i = 0 ; i < rv.mixRing.size() ; i++) { - tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); - addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); - DP(tmpb); - if (!tmpb) { + + { + boost::asio::io_service ioservice; + boost::thread_group threadpool; + std::unique_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(ioservice)); + size_t threads = tools::get_max_concurrency(); + threads = std::min(threads, rv.mixRing.size()); + for (size_t i = 0; i < threads; ++i) + threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice)); + bool ioservice_active = threads > 1; + std::deque<bool> results(rv.mixRing.size(), false); + epee::misc_utils::auto_scope_leave_caller ioservice_killer = epee::misc_utils::create_scope_leave_handler([&]() { KILL_IOSERVICE(); }); + + for (i = 0 ; i < rv.mixRing.size() ; i++) { + if (threads > 1) { + ioservice.dispatch(boost::bind(&verRctMGSimpleWrapper, std::cref(message), std::cref(rv.p.MGs[i]), std::cref(rv.mixRing[i]), std::cref(rv.pseudoOuts[i]), std::ref(results[i]))); + } + else { + bool tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + DP(tmpb); + if (!tmpb) { + LOG_ERROR("verRctMGSimple failed for input " << i); + return false; + } + } + } + KILL_IOSERVICE(); + if (threads > 1) { + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { LOG_ERROR("verRctMGSimple failed for input " << i); return false; + } } + } + } + + key sumPseudoOuts = identity(); + for (i = 0 ; i < rv.mixRing.size() ; i++) { + addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); } DP(sumPseudoOuts); diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 11d771818..a4fecade4 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -70,7 +70,7 @@ namespace rct { //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 //These are called in the below ASNL sig generation - void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index); + void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, unsigned int index); bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2); //Aggregate Schnorr Non-linkable Ring Signature (ASNL) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index e8b2d5cb1..f5258268c 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1236,10 +1236,10 @@ namespace cryptonote return false; } - std::map<uint64_t, uint64_t> histogram; + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram; try { - histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked); + histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff); } catch (const std::exception &e) { @@ -1251,8 +1251,8 @@ namespace cryptonote res.histogram.reserve(histogram.size()); for (const auto &i: histogram) { - if (i.second >= req.min_count && (i.second <= req.max_count || req.max_count == 0)) - res.histogram.push_back(COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry(i.first, i.second)); + if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0)) + res.histogram.push_back(COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry(i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second))); } res.status = CORE_RPC_STATUS_OK; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 61c302e45..135cf1fe7 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1172,26 +1172,33 @@ namespace cryptonote uint64_t min_count; uint64_t max_count; bool unlocked; + uint64_t recent_cutoff; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts); KV_SERIALIZE(min_count); KV_SERIALIZE(max_count); KV_SERIALIZE(unlocked); + KV_SERIALIZE(recent_cutoff); END_KV_SERIALIZE_MAP() }; struct entry { uint64_t amount; - uint64_t instances; + uint64_t total_instances; + uint64_t unlocked_instances; + uint64_t recent_instances; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount); - KV_SERIALIZE(instances); + KV_SERIALIZE(total_instances); + KV_SERIALIZE(unlocked_instances); + KV_SERIALIZE(recent_instances); END_KV_SERIALIZE_MAP() - entry(uint64_t amount, uint64_t instances): amount(amount), instances(instances) {} + entry(uint64_t amount, uint64_t total_instances, uint64_t unlocked_instances, uint64_t recent_instances): + amount(amount), total_instances(total_instances), unlocked_instances(unlocked_instances), recent_instances(recent_instances) {} entry() {} }; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 19dc7d197..22f0e3b6d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -873,65 +873,79 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::ask_wallet_create_if_needed() { + LOG_PRINT_L3("simple_wallet::ask_wallet_create_if_needed() started"); std::string wallet_path; - - bool valid_path = false; - do { - wallet_path = command_line::input_line( - tr("Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" - "Wallet file name: ") - ); - if (std::cin.eof()) - { - return false; - } - valid_path = tools::wallet2::wallet_valid_path_format(wallet_path); - if (!valid_path) - { - fail_msg_writer() << tr("wallet file path not valid: ") << wallet_path; - } - } - while (!valid_path); - + std::string confirm_creation; + bool wallet_name_valid = false; bool keys_file_exists; bool wallet_file_exists; - tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exists); - LOG_PRINT_L3("wallet_path: " << wallet_path << ""); - LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha - << " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha); - LOG_PRINT_L1("Loading wallet..."); - - // add logic to error out if new wallet requested but named wallet file exists - if (keys_file_exists || wallet_file_exists) - { - if (!m_generate_new.empty() || m_restoring) - { - fail_msg_writer() << tr("attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting."); - return false; - } - } + do{ + LOG_PRINT_L3("User asked to specify wallet file name."); + wallet_path = command_line::input_line( + tr("Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" + "Wallet file name (or Ctrl-C to quit): ") + ); + if(std::cin.eof()) + { + LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()"); + return false; + } + if(!tools::wallet2::wallet_valid_path_format(wallet_path)) + { + fail_msg_writer() << tr("Wallet name not valid. Please try again or use Ctrl-C to quit."); + wallet_name_valid = false; + } + else + { + tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exists); + LOG_PRINT_L3("wallet_path: " << wallet_path << ""); + LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha + << " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha); - bool r; - if(keys_file_exists) - { - m_wallet_file=wallet_path; - r = true; - }else - { - if(!wallet_file_exists) - { - std::cout << tr("The wallet doesn't exist, generating new one") << std::endl; - m_generate_new = wallet_path; - r = true; - }else - { - fail_msg_writer() << tr("keys file not found: failed to open wallet: ") << "\"" << wallet_path << "\"."; - r = false; - } - } + if((keys_file_exists || wallet_file_exists) && (!m_generate_new.empty() || m_restoring)) + { + fail_msg_writer() << tr("Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting."); + return false; + } + if(wallet_file_exists && keys_file_exists) //Yes wallet, yes keys + { + success_msg_writer() << tr("Wallet and key files found, loading..."); + m_wallet_file = wallet_path; + return true; + } + else if(!wallet_file_exists && keys_file_exists) //No wallet, yes keys + { + success_msg_writer() << tr("Key file found but not wallet file. Regenerating..."); + m_wallet_file = wallet_path; + return true; + } + else if(wallet_file_exists && !keys_file_exists) //Yes wallet, no keys + { + fail_msg_writer() << tr("Key file not found. Failed to open wallet: ") << "\"" << wallet_path << "\". Exiting."; + return false; + } + else if(!wallet_file_exists && !keys_file_exists) //No wallet, no keys + { + message_writer() << tr("No wallet/key file found with that name. Confirm creation of new wallet named: ") << wallet_path; + confirm_creation = command_line::input_line(tr("(y)es/(n)o: ")); + if(std::cin.eof()) + { + LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()"); + return false; + } + if(is_it_true(confirm_creation)) + { + success_msg_writer() << tr("Generating new wallet..."); + m_generate_new = wallet_path; + return true; + } + } + } + } while(!wallet_name_valid); - return r; + LOG_ERROR("Failed out of do-while loop in ask_wallet_create_if_needed()"); + return false; } /*! @@ -3867,8 +3881,8 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) m_wallet->get_payments_out(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { const tools::wallet2::confirmed_transfer_details &pd = i->second; - uint64_t fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known + uint64_t fee = pd.m_amount_in - pd.m_amount_out - change; std::string dests; for (const auto &d: pd.m_dests) { if (!dests.empty()) diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index e4a003b02..2ba5f3620 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -156,8 +156,8 @@ void TransactionHistoryImpl::refresh() const crypto::hash &hash = i->first; const tools::wallet2::confirmed_transfer_details &pd = i->second; - uint64_t fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known + uint64_t fee = pd.m_amount_in - pd.m_amount_out - change; std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f589b0821..4d863d34d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -77,6 +77,9 @@ using namespace cryptonote; #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001" #define SIGNED_TX_PREFIX "Monero signed tx set\001" +#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone +#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone + #define KILL_IOSERVICE() \ do { \ work.reset(); \ @@ -2855,6 +2858,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si auto end = std::unique(req_t.params.amounts.begin(), req_t.params.amounts.end()); req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end)); req_t.params.unlocked = true; + req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); @@ -2880,18 +2884,33 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si // if there are just enough outputs to mix with, use all of them. // Eventually this should become impossible. - uint64_t num_outs = 0; + uint64_t num_outs = 0, num_recent_outs = 0; for (auto he: resp_t.result.histogram) { if (he.amount == amount) { - num_outs = he.instances; + LOG_PRINT_L2("Found " << print_money(amount) << ": " << he.total_instances << " total, " + << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent"); + num_outs = he.unlocked_instances; + num_recent_outs = he.recent_instances; break; } } LOG_PRINT_L1("" << num_outs << " outputs of size " << print_money(amount)); THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, "histogram reports no outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours"); + THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error, + "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount)); + + // X% of those outs are to be taken from recent outputs + size_t recent_outputs_count = requested_outputs_count * RECENT_OUTPUT_RATIO; + if (recent_outputs_count == 0) + recent_outputs_count = 1; // ensure we have at least one, if possible + if (recent_outputs_count > num_recent_outs) + recent_outputs_count = num_recent_outs; + if (td.m_global_output_index >= num_outs - num_recent_outs) + --recent_outputs_count; // if the real out is recent, pick one less recent fake out + LOG_PRINT_L1("Using " << recent_outputs_count << " recent outputs"); if (num_outs <= requested_outputs_count) { @@ -2921,11 +2940,24 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si // return to the top of the loop and try again, otherwise add it to the // list of output indices we've seen. - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt + uint64_t i; + if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with + { + // equiprobable distribution over the recent outs + uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs; + LOG_PRINT_L2("picking " << i << " as recent"); + } + else + { + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + i = (uint64_t)(frac*num_outs); + LOG_PRINT_L2("picking " << i << " as triangular"); + } + // just in case rounding up to 1 occurs after calc if (i == num_outs) --i; @@ -3981,7 +4013,7 @@ uint64_t wallet2::get_num_rct_outputs() THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response"); THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount"); - return resp_t.result.histogram[0].instances; + return resp_t.result.histogram[0].total_instances; } //---------------------------------------------------------------------------------------------------- std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a039c92d6..4777102af 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -422,7 +422,6 @@ namespace tools * \return Whether path is valid format */ static bool wallet_valid_path_format(const std::string& file_path); - static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index b3bd47687..7b4874811 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -106,7 +106,7 @@ public: virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const { return true; } virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const { return true; } virtual bool is_read_only() const { return false; } - virtual std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked) const { return std::map<uint64_t, uint64_t>(); } + virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); } virtual void add_block( const block& blk , const size_t& block_size |