aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/blockchain_db/blockchain_db.h3
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp36
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h3
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/blockchain.cpp4
-rw-r--r--src/cryptonote_core/blockchain.h3
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp2
-rw-r--r--src/cryptonote_core/cryptonote_core.h2
-rw-r--r--src/daemon/rpc_command_executor.cpp6
-rw-r--r--src/ringct/rctOps.cpp31
-rw-r--r--src/ringct/rctOps.h4
-rw-r--r--src/ringct/rctSigs.cpp142
-rw-r--r--src/ringct/rctSigs.h2
-rw-r--r--src/rpc/core_rpc_server.cpp8
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h13
-rw-r--r--src/simplewallet/simplewallet.cpp122
-rw-r--r--src/wallet/api/transaction_history.cpp2
-rw-r--r--src/wallet/wallet2.cpp48
-rw-r--r--src/wallet/wallet2.h1
-rw-r--r--tests/unit_tests/hardfork.cpp2
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