aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/common/CMakeLists.txt4
-rw-r--r--src/common/perf_timer.cpp47
-rw-r--r--src/common/perf_timer.h92
-rw-r--r--src/cryptonote_core/blockchain.cpp7
-rw-r--r--src/cryptonote_core/blockchain.h3
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp28
-rw-r--r--src/cryptonote_core/cryptonote_core.h7
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp3
-rw-r--r--src/cryptonote_core/tx_pool.cpp2
-rw-r--r--src/daemon/command_parser_executor.cpp22
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp5
-rw-r--r--src/daemon/rpc_command_executor.cpp41
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/ringct/rctSigs.cpp11
-rw-r--r--src/ringct/rctSigs.h4
-rw-r--r--src/ringct/rctTypes.h2
-rw-r--r--src/rpc/core_rpc_server.cpp16
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h41
-rw-r--r--src/simplewallet/simplewallet.cpp2
-rw-r--r--src/wallet/api/transaction_history.cpp41
-rw-r--r--src/wallet/api/transaction_history.h2
-rw-r--r--src/wallet/api/wallet.cpp43
-rw-r--r--src/wallet/api/wallet.h5
-rw-r--r--src/wallet/api/wallet_manager.cpp5
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/wallet2.cpp309
-rw-r--r--src/wallet/wallet2.h3
-rw-r--r--src/wallet/wallet2_api.h24
33 files changed, 528 insertions, 291 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/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 6bf8b1777..4b6149cbd 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -31,7 +31,8 @@ set(common_sources
command_line.cpp
dns_utils.cpp
util.cpp
- i18n.cpp)
+ i18n.cpp
+ perf_timer.cpp)
if (STACK_TRACE)
list(APPEND common_sources stack_trace.cpp)
@@ -53,6 +54,7 @@ set(common_private_headers
util.h
varint.h
i18n.h
+ perf_timer.h
stack_trace.h)
monero_private_headers(common
diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp
new file mode 100644
index 000000000..d23c9f11d
--- /dev/null
+++ b/src/common/perf_timer.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, 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 "perf_timer.h"
+
+namespace tools
+{
+
+int performance_timer_log_level = 2;
+__thread std::vector<PerformanceTimer*> *performance_timers = NULL;
+
+void set_performance_timer_log_level(int level)
+{
+ if (level < LOG_LEVEL_MIN || level > LOG_LEVEL_MAX)
+ {
+ LOG_PRINT_L0("Wrong log level: " << level << ", using 2");
+ level = 2;
+ }
+ performance_timer_log_level = level;
+}
+
+}
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
new file mode 100644
index 000000000..3d732012d
--- /dev/null
+++ b/src/common/perf_timer.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, 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
+
+#include <string>
+#include <stdio.h>
+#include "misc_log_ex.h"
+
+namespace tools
+{
+
+class PerformanceTimer;
+
+extern int performance_timer_log_level;
+extern __thread std::vector<PerformanceTimer*> *performance_timers;
+
+class PerformanceTimer
+{
+public:
+ PerformanceTimer(const std::string &s, int l = LOG_LEVEL_2): name(s), level(l), started(false)
+ {
+ ticks = epee::misc_utils::get_tick_count();
+ if (!performance_timers)
+ {
+ LOG_PRINT("PERF ----------", level);
+ performance_timers = new std::vector<PerformanceTimer*>();
+ }
+ else
+ {
+ PerformanceTimer *pt = performance_timers->back();
+ if (!pt->started)
+ {
+ LOG_PRINT("PERF " << std::string((performance_timers->size()-1) * 2, ' ') << " " << pt->name, pt->level);
+ pt->started = true;
+ }
+ }
+ performance_timers->push_back(this);
+ }
+
+ ~PerformanceTimer()
+ {
+ performance_timers->pop_back();
+ ticks = epee::misc_utils::get_tick_count() - ticks;
+ char s[12];
+ snprintf(s, sizeof(s), "%8lu ", ticks);
+ LOG_PRINT("PERF " << s << std::string(performance_timers->size() * 2, ' ') << " " << name, level);
+ if (performance_timers->empty())
+ {
+ delete performance_timers;
+ performance_timers = NULL;
+ }
+ }
+
+private:
+ std::string name;
+ int level;
+ uint64_t ticks;
+ bool started;
+};
+
+void set_performance_timer_log_level(int level);
+
+#define PERF_TIMER(name) tools::PerformanceTimer pt_##name(#name, tools::performance_timer_log_level)
+#define PERF_TIMER_L(name, l) tools::PerformanceTimer pt_##name(#name, l)
+
+}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 83290796b..9ea023a4c 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -52,6 +52,7 @@
#include "cryptonote_core/checkpoints.h"
#include "cryptonote_core/cryptonote_core.h"
#include "ringct/rctSigs.h"
+#include "common/perf_timer.h"
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
#endif
@@ -2245,6 +2246,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
}
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys)
{
+ PERF_TIMER(expand_transaction_2);
CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2");
rct::rctSig &rv = tx.rct_signatures;
@@ -2321,6 +2323,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// using threads, etc.)
bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
{
+ PERF_TIMER(check_tx_inputs);
LOG_PRINT_L3("Blockchain::" << __func__);
size_t sig_index = 0;
if(pmax_used_block_height)
@@ -3754,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 149fb09df..a82d96416 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -616,6 +616,34 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ 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;
+ std::list<crypto::hash> missed_txs;
+ uint64_t coinbase_amount = 0;
+ uint64_t emission_amount = 0;
+ uint64_t total_fee_amount = 0;
+ uint64_t tx_fee_amount = 0;
+ this->get_blocks(start_offset, count, blocks);
+ BOOST_FOREACH(auto& b, blocks)
+ {
+ coinbase_amount = get_outs_money_amount(b.miner_tx);
+ this->get_transactions(b.tx_hashes, txs, missed_txs);
+ BOOST_FOREACH(const auto& tx, txs)
+ {
+ tx_fee_amount += get_tx_fee(tx);
+ }
+
+ emission_amount += coinbase_amount - tx_fee_amount;
+ total_fee_amount += tx_fee_amount;
+ coinbase_amount = 0;
+ tx_fee_amount = 0;
+ }
+
+ return std::pair<uint64_t, uint64_t>(emission_amount, total_fee_amount);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_diff(const transaction& tx) const
{
std::unordered_set<crypto::key_image> ki;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 6727d6b04..d925a184d 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -600,6 +600,13 @@ namespace cryptonote
*/
size_t get_block_sync_size() const { return block_sync_size; }
+ /**
+ * @brief get the sum of coinbase tx amounts between blocks
+ *
+ * @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 size_t count);
+
private:
/**
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index 64f8eb924..870e8f0d8 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -658,10 +658,7 @@ namespace cryptonote
}
else
{
- bool all_rct_inputs = true;
size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
- BOOST_FOREACH(const tx_source_entry& src_entr, sources)
- all_rct_inputs &= !(src_entr.mask == rct::identity());
// 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.
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 5bfa7eca6..178cbda3c 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -42,6 +42,7 @@
#include "common/int-util.h"
#include "misc_language.h"
#include "warnings.h"
+#include "common/perf_timer.h"
#include "crypto/hash.h"
DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated
@@ -78,6 +79,7 @@ namespace cryptonote
//---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const 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, uint8_t version)
{
+ PERF_TIMER(add_tx);
if (tx.version == 0)
{
// v0 never accepted
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 6ea862b56..5d7ed6cc0 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -452,5 +452,27 @@ bool t_command_parser_executor::output_histogram(const std::vector<std::string>&
return m_executor.output_histogram(min_count, max_count);
}
+bool t_command_parser_executor::print_coinbase_tx_sum(const std::vector<std::string>& args)
+{
+ if(!args.size())
+ {
+ std::cout << "need block height parameter" << std::endl;
+ return false;
+ }
+ uint64_t height = 0;
+ uint64_t count = 0;
+ if(!epee::string_tools::get_xtype_from_string(height, args[0]))
+ {
+ std::cout << "wrong starter block height parameter" << std::endl;
+ return false;
+ }
+ if(args.size() >1 && !epee::string_tools::get_xtype_from_string(count, args[1]))
+ {
+ std::cout << "wrong count parameter" << std::endl;
+ return false;
+ }
+
+ return m_executor.print_coinbase_tx_sum(height, count);
+}
} // namespace daemonize
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 7819bd261..6a984aa71 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -115,6 +115,8 @@ public:
bool flush_txpool(const std::vector<std::string>& args);
bool output_histogram(const std::vector<std::string>& args);
+
+ bool print_coinbase_tx_sum(const std::vector<std::string>& args);
};
} // namespace daemonize
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index cb54d1966..1418b920f 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -215,6 +215,11 @@ t_command_server::t_command_server(
, std::bind(&t_command_parser_executor::output_histogram, &m_parser, p::_1)
, "Print output histogram (amount, instances)"
);
+ m_command_lookup.set_handler(
+ "print_coinbase_tx_sum"
+ , std::bind(&t_command_parser_executor::print_coinbase_tx_sum, &m_parser, p::_1)
+ , "Print sum of coinbase transactions (start height, block count)"
+ );
}
bool t_command_server::process_command_str(const std::string& cmd)
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index e7229f7f9..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,14 +1263,49 @@ 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;
}
+bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t count)
+{
+ cryptonote::COMMAND_RPC_GET_COINBASE_TX_SUM::request req;
+ cryptonote::COMMAND_RPC_GET_COINBASE_TX_SUM::response res;
+ epee::json_rpc::error error_resp;
+
+ req.height = height;
+ req.count = count;
+
+ std::string fail_message = "Unsuccessful";
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "get_coinbase_tx_sum", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp))
+ {
+ tools::fail_msg_writer() << fail_message.c_str();
+ return true;
+ }
+ }
+
+ tools::msg_writer() << "Sum of coinbase transactions between block heights ["
+ << height << ", " << (height + count) << ") is "
+ << cryptonote::print_money(res.emission_amount + res.fee_amount) << " "
+ << "consisting of " << cryptonote::print_money(res.emission_amount)
+ << " in emissions, and " << cryptonote::print_money(res.fee_amount) << " in fees";
+ return true;
+}
+
}// namespace daemonize
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 5eed44353..c097453e7 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -133,6 +133,8 @@ public:
bool flush_txpool(const std::string &txid);
bool output_histogram(uint64_t min_count, uint64_t max_count);
+
+ bool print_coinbase_tx_sum(uint64_t height, uint64_t count);
};
} // namespace daemonize
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index ed1f8cc0e..80ece9c44 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -29,6 +29,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "misc_log_ex.h"
+#include "common/perf_timer.h"
#include "rctSigs.h"
#include "cryptonote_core/cryptonote_format_utils.h"
@@ -107,6 +108,7 @@ namespace rct {
// an x[i] such that x[i]G = one of P1[i] or P2[i]
// Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i
bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as) {
+ PERF_TIMER(VerASNL);
DP("Verifying Aggregate Schnorr Non-linkable Ring Signature\n");
key LHS = identity();
key RHS = scalarmultBase(as.s);
@@ -150,7 +152,7 @@ namespace rct {
// Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
- mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) {
+ mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) {
mgSig rv;
size_t cols = pk.size();
CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!");
@@ -239,7 +241,7 @@ namespace rct {
// Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
- bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) {
+ bool MLSAG_Ver(const key &message, const keyM & pk, const mgSig & rv, size_t dsRows) {
size_t cols = pk.size();
CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!");
@@ -331,6 +333,7 @@ namespace rct {
// mask is a such that C = aG + bH, and b = amount
//verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
bool verRange(const key & C, const rangeSig & as) {
+ PERF_TIMER(verRange);
key64 CiH;
int i = 0;
key Ctmp = identity();
@@ -467,6 +470,7 @@ namespace rct {
//Ver:
// verifies the above sig is created corretly
bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) {
+ PERF_TIMER(verRctMG);
//setup vars
size_t cols = pubs.size();
CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
@@ -505,6 +509,7 @@ namespace rct {
//This does a simplified version, assuming only post Rct
//inputs
bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) {
+ PERF_TIMER(verRctMGSimple);
//setup vars
size_t rows = 1;
size_t cols = pubs.size();
@@ -729,6 +734,7 @@ namespace rct {
// 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
bool verRct(const rctSig & rv) {
+ PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
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");
@@ -769,6 +775,7 @@ namespace rct {
//ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity)
bool verRctSimple(const rctSig & rv) {
+ PERF_TIMER(verRctSimple);
size_t i = 0;
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig");
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index f1c906d5e..11d771818 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -90,8 +90,8 @@ namespace rct {
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
keyV keyImageV(const keyV &xx);
- mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows);
- bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, size_t dsRows);
+ mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows);
+ bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &sig, size_t dsRows);
//mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
//proveRange and verRange
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index bfafebb83..25f6f9bc9 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -415,7 +415,7 @@ namespace rct {
// then the value in the first 8 bytes is returned
xmr_amount h2d(const key &test);
//32 byte key to int[64]
- void h2b(bits amountb2, key & test);
+ void h2b(bits amountb2, const key & test);
//int[64] to 32 byte key
void b2h(key & amountdh, bits amountb2);
//int[64] to uint long long
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 700019250..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;
@@ -1266,6 +1266,14 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::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)
+ {
+ std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
+ res.emission_amount = amounts.first;
+ res.fee_amount = amounts.second;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res)
{
// TODO
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 68da59f6b..147f019d6 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -115,6 +115,7 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
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_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -160,6 +161,7 @@ namespace cryptonote
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::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_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);
//-----------------------
private:
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index dd2116e51..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() {}
};
@@ -1226,5 +1233,31 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
-}
+ struct COMMAND_RPC_GET_COINBASE_TX_SUM
+ {
+ struct request
+ {
+ uint64_t height;
+ uint64_t count;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(height);
+ KV_SERIALIZE(count);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ uint64_t emission_amount;
+ uint64_t fee_amount;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(emission_amount)
+ KV_SERIALIZE(fee_amount)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+}
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index eced4537f..639fcb4b6 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -678,7 +678,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm"));
m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]"));
m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0"));
- m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address"));
+ m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance an address"));
m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file"));
m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file"));
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log <level> - Change current log detail level, <0-4>"));
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 95aafb04f..e4a003b02 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -60,21 +60,44 @@ TransactionHistoryImpl::~TransactionHistoryImpl()
int TransactionHistoryImpl::count() const
{
- return m_history.size();
+ boost::shared_lock<boost::shared_mutex> lock(m_historyMutex);
+ int result = m_history.size();
+ return result;
+}
+
+TransactionInfo *TransactionHistoryImpl::transaction(int index) const
+{
+ boost::shared_lock<boost::shared_mutex> lock(m_historyMutex);
+ // sanity check
+ if (index < 0)
+ return nullptr;
+ unsigned index_ = static_cast<unsigned>(index);
+ return index_ < m_history.size() ? m_history[index_] : nullptr;
}
TransactionInfo *TransactionHistoryImpl::transaction(const std::string &id) const
{
- return nullptr;
+ boost::shared_lock<boost::shared_mutex> lock(m_historyMutex);
+ auto itr = std::find_if(m_history.begin(), m_history.end(),
+ [&](const TransactionInfo * ti) {
+ return ti->hash() == id;
+ });
+ return itr != m_history.end() ? *itr : nullptr;
}
std::vector<TransactionInfo *> TransactionHistoryImpl::getAll() const
{
+ boost::shared_lock<boost::shared_mutex> lock(m_historyMutex);
return m_history;
}
void TransactionHistoryImpl::refresh()
{
+ // multithreaded access:
+ // boost::lock_guard<boost::mutex> guarg(m_historyMutex);
+ // for "write" access, locking exclusively
+ boost::unique_lock<boost::shared_mutex> lock(m_historyMutex);
+
// TODO: configurable values;
uint64_t min_height = 0;
uint64_t max_height = (uint64_t)-1;
@@ -84,8 +107,6 @@ void TransactionHistoryImpl::refresh()
delete t;
m_history.clear();
-
-
// transactions are stored in wallet2:
// - confirmed_transfer_details - out transfers
// - unconfirmed_transfer_details - pending out transfers
@@ -109,7 +130,7 @@ void TransactionHistoryImpl::refresh()
ti->m_hash = string_tools::pod_to_hex(pd.m_tx_hash);
ti->m_blockheight = pd.m_block_height;
// TODO:
- // ti->m_timestamp = pd.m_timestamp;
+ ti->m_timestamp = pd.m_timestamp;
m_history.push_back(ti);
/* output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%20.20s %s %s %s")
@@ -151,6 +172,7 @@ void TransactionHistoryImpl::refresh()
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_hash = string_tools::pod_to_hex(hash);
ti->m_blockheight = pd.m_block_height;
+ ti->m_timestamp = pd.m_timestamp;
// single output transaction might contain multiple transfers
for (const auto &d: pd.m_dests) {
@@ -180,14 +202,9 @@ void TransactionHistoryImpl::refresh()
ti->m_failed = is_failed;
ti->m_pending = true;
ti->m_hash = string_tools::pod_to_hex(hash);
+ ti->m_timestamp = pd.m_timestamp;
m_history.push_back(ti);
}
-
-}
-
-TransactionInfo *TransactionHistoryImpl::transaction(int index) const
-{
- return nullptr;
}
-}
+} // namespace
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index 171fd2210..1b6617ead 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "wallet/wallet2_api.h"
+#include <boost/thread/shared_mutex.hpp>
namespace Bitmonero {
@@ -51,6 +52,7 @@ private:
// TransactionHistory is responsible of memory management
std::vector<TransactionInfo*> m_history;
WalletImpl *m_wallet;
+ mutable boost::shared_mutex m_historyMutex;
};
}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 4d35bc404..d1c849537 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -54,8 +54,9 @@ namespace {
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
{
- Wallet2CallbackImpl()
+ Wallet2CallbackImpl(WalletImpl * wallet)
: m_listener(nullptr)
+ , m_wallet(wallet)
{
}
@@ -93,7 +94,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
- if (m_listener) {
+ // do not signal on received tx if wallet is not syncronized completely
+ if (m_listener && m_wallet->synchronized()) {
m_listener->moneyReceived(tx_hash, amount);
m_listener->updated();
}
@@ -107,7 +109,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
- if (m_listener) {
+ // do not signal on sent tx if wallet is not syncronized completely
+ if (m_listener && m_wallet->synchronized()) {
m_listener->moneySpent(tx_hash, amount);
m_listener->updated();
}
@@ -119,6 +122,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
WalletListener * m_listener;
+ WalletImpl * m_wallet;
};
Wallet::~Wallet() {}
@@ -166,12 +170,16 @@ uint64_t Wallet::maximumAllowedAmount()
///////////////////////// WalletImpl implementation ////////////////////////
WalletImpl::WalletImpl(bool testnet)
- :m_wallet(nullptr), m_status(Wallet::Status_Ok), m_trustedDaemon(false),
- m_wallet2Callback(nullptr), m_recoveringFromSeed(false)
+ :m_wallet(nullptr)
+ , m_status(Wallet::Status_Ok)
+ , m_trustedDaemon(false)
+ , m_wallet2Callback(nullptr)
+ , m_recoveringFromSeed(false)
+ , m_synchronized(false)
{
m_wallet = new tools::wallet2(testnet);
m_history = new TransactionHistoryImpl(this);
- m_wallet2Callback = new Wallet2CallbackImpl;
+ m_wallet2Callback = new Wallet2CallbackImpl(this);
m_wallet->callback(m_wallet2Callback);
m_refreshThreadDone = false;
m_refreshEnabled = false;
@@ -201,7 +209,6 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co
bool keys_file_exists;
bool wallet_file_exists;
tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
- // TODO: figure out how to setup logger;
LOG_PRINT_L3("wallet_path: " << path << "");
LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
<< " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
@@ -404,7 +411,15 @@ void WalletImpl::initAsync(const string &daemon_address, uint64_t upper_transact
startRefresh();
}
+void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height)
+{
+ m_wallet->set_refresh_from_block_height(refresh_from_block_height);
+}
+void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed)
+{
+ m_recoveringFromSeed = recoveringFromSeed;
+}
uint64_t WalletImpl::balance() const
{
@@ -455,6 +470,11 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const
return result;
}
+bool WalletImpl::synchronized() const
+{
+ return m_synchronized;
+}
+
bool WalletImpl::refresh()
{
clearStatus();
@@ -723,6 +743,15 @@ void WalletImpl::doRefresh()
boost::lock_guard<boost::mutex> guarg(m_refreshMutex2);
try {
m_wallet->refresh();
+ if (!m_synchronized) {
+ m_synchronized = true;
+ }
+ // assuming if we have empty history, it wasn't initialized yet
+ // for futher history changes client need to update history in
+ // "on_money_received" and "on_money_sent" callbacks
+ if (m_history->count() == 0) {
+ m_history->refresh();
+ }
} catch (const std::exception &e) {
m_status = Status_Error;
m_errorString = e.what();
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index c399e3ab6..c8a59f7c3 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -78,10 +78,13 @@ public:
uint64_t blockChainHeight() const;
uint64_t daemonBlockChainHeight() const;
uint64_t daemonBlockChainTargetHeight() const;
+ bool synchronized() const;
bool refresh();
void refreshAsync();
void setAutoRefreshInterval(int millis);
int autoRefreshInterval() const;
+ void setRefreshFromBlockHeight(uint64_t refresh_from_block_height);
+ void setRecoveringFromSeed(bool recoveringFromSeed);
@@ -108,6 +111,7 @@ private:
private:
friend class PendingTransactionImpl;
friend class TransactionHistoryImpl;
+ friend class Wallet2CallbackImpl;
tools::wallet2 * m_wallet;
mutable std::atomic<int> m_status;
@@ -133,6 +137,7 @@ private:
// so it shouldn't be considered as new and pull blocks (slow-refresh)
// instead of pulling hashes (fast-refresh)
bool m_recoveringFromSeed;
+ std::atomic<bool> m_synchronized;
};
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index ca83806ff..aa99476ee 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -57,9 +57,12 @@ Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string
return wallet;
}
-Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &memo, bool testnet)
+Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &memo, bool testnet, uint64_t restoreHeight)
{
WalletImpl * wallet = new WalletImpl(testnet);
+ if(restoreHeight > 0){
+ wallet->setRefreshFromBlockHeight(restoreHeight);
+ }
wallet->recover(path, memo);
return wallet;
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 7585c1af7..2e932a2a1 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -40,7 +40,7 @@ public:
Wallet * createWallet(const std::string &path, const std::string &password,
const std::string &language, bool testnet);
Wallet * openWallet(const std::string &path, const std::string &password, bool testnet);
- virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet);
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet, uint64_t restoreHeight);
virtual bool closeWallet(Wallet *wallet);
bool walletExists(const std::string &path);
std::vector<std::string> findWallets(const std::string &path);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e3736bc3d..6a60ebe28 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(); \
@@ -583,11 +586,14 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
std::string(", expected ") + print_money(td.amount()));
}
amount = td.amount();
- LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid());
tx_money_spent_in_ins += amount;
- set_spent(it->second, height);
- if (0 != m_callback)
- m_callback->on_money_spent(height, tx, amount, tx);
+ if (!pool)
+ {
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid());
+ set_spent(it->second, height);
+ if (0 != m_callback)
+ m_callback->on_money_spent(height, tx, amount, tx);
+ }
}
}
@@ -2852,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");
@@ -2877,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)
{
@@ -2918,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;
@@ -3662,6 +3697,26 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
+ const bool use_rct = use_fork_rules(4, 0);
+
+ // gather all our dust and non dust outputs
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
+ {
+ if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
+ unused_transfers_indices.push_back(i);
+ else
+ unused_dust_indices.push_back(i);
+ }
+ }
+
+ return create_transactions_from(address, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
+}
+
+std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon)
+{
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
std::list<size_t> selected_transfers;
@@ -3673,24 +3728,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
- const bool use_rct = use_fork_rules(4, 0);
+ const bool use_rct = use_fork_rules(4, 0);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
- // gather all our dust and non dust outputs
- for (size_t i = 0; i < m_transfers.size(); ++i)
- {
- const transfer_details& td = m_transfers[i];
- if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
- {
- if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
- else
- unused_dust_indices.push_back(i);
- }
- }
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
if (unused_dust_indices.empty() && unused_transfers_indices.empty())
@@ -3820,121 +3863,6 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
}
return money;
}
-
-template<typename T>
-void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
-{
- using namespace cryptonote;
-
- uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
-
- // select all dust inputs for transaction
- // throw if there are none
- uint64_t money = 0;
- std::list<size_t> selected_transfers;
-#if 1
- for (size_t n = 0; n < outs.size(); ++n)
- {
- const transfer_details& td = m_transfers[outs[n]];
- if (!td.m_spent)
- {
- selected_transfers.push_back (outs[n]);
- money += td.amount();
- if (selected_transfers.size() >= num_outputs)
- break;
- }
- }
-#else
- for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
- {
- const transfer_details& td = *i;
- if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
- {
- selected_transfers.push_back (i);
- money += td.amount();
- if (selected_transfers.size() >= num_outputs)
- break;
- }
- }
-#endif
-
- // we don't allow no output to self, easier, but one may want to burn the dust if = fee
- THROW_WALLET_EXCEPTION_IF(money <= needed_fee, error::not_enough_money, money, needed_fee, needed_fee);
-
- typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
-
- //prepare inputs
- size_t i = 0;
- std::vector<cryptonote::tx_source_entry> sources;
- BOOST_FOREACH(size_t idx, selected_transfers)
- {
- sources.resize(sources.size()+1);
- cryptonote::tx_source_entry& src = sources.back();
- const transfer_details& td = m_transfers[idx];
- src.amount = td.amount();
- src.rct = td.is_rct();
-
- //paste real transaction to the random index
- auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
- {
- return a.first >= td.m_global_output_index;
- });
- tx_output_entry real_oe;
- real_oe.first = td.m_global_output_index;
- real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
- real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
- auto interted_it = src.outputs.insert(it_to_insert, real_oe);
- src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
- src.real_output = interted_it - src.outputs.begin();
- src.real_output_in_tx_index = td.m_internal_output_index;
- detail::print_source_entry(src);
- ++i;
- }
-
- cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
-
- std::vector<cryptonote::tx_destination_entry> dsts;
- uint64_t money_back = money - needed_fee;
- if (dust_policy.dust_threshold > 0)
- money_back = money_back - money_back % dust_policy.dust_threshold;
- dsts.push_back(cryptonote::tx_destination_entry(money_back, m_account_public_address));
- std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust;
- destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust);
- BOOST_FOREACH(auto& d, dust) {
- THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
- std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
- }
-
- crypto::secret_key tx_key;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
-
- 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
- {
- CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
- key_images += boost::to_string(in.k_image) + " ";
- return true;
- });
- THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
-
- ptx.key_images = key_images;
- ptx.fee = money - money_back;
- ptx.dust = 0;
- ptx.tx = tx;
- ptx.change_dts = change_dts;
- ptx.selected_transfers = selected_transfers;
- ptx.tx_key = tx_key;
- ptx.dests = dsts;
- ptx.construction_data.sources = sources;
- ptx.construction_data.destinations = dsts;
- ptx.construction_data.change_dts = change_dts;
- ptx.construction_data.extra = tx.extra;
- ptx.construction_data.unlock_time = unlock_time;
- ptx.construction_data.use_rct = false;
-}
-
//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
{
@@ -4079,7 +4007,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)
@@ -4114,100 +4042,17 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
return std::vector<wallet2::pending_tx>();
}
- // failsafe split attempt counter
- size_t attempt_count = 0;
-
- for(attempt_count = 1; ;attempt_count++)
+ // split in "dust" and "non dust" to make it easier to select outputs
+ std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
+ for (auto n: unmixable_outputs)
{
- size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
- size_t num_outputs_per_tx = (num_dust_outputs + num_tx - 1) / num_tx;
-
- std::vector<pending_tx> ptx_vector;
- try
- {
- // for each new tx
- for (size_t i=0; i<num_tx;++i)
- {
- cryptonote::transaction tx;
- pending_tx ptx;
- std::vector<uint8_t> extra;
-
- // loop until fee is met without increasing tx size to next KB boundary.
- uint64_t needed_fee = 0;
- do
- {
- transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
- auto txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, 1);
-
- // reroll the tx with the actual amount minus the fee
- // if there's not enough for the fee, it'll throw
- transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
- txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, 1);
- } while (ptx.fee < needed_fee);
-
- ptx_vector.push_back(ptx);
-
- // mark transfers to be used as "spent"
- BOOST_FOREACH(size_t idx, ptx.selected_transfers)
- {
- set_spent(idx, 0);
- }
- }
-
- // if we made it this far, we've selected our transactions. committing them will mark them spent,
- // so this is a failsafe in case they don't go through
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- // if we made it this far, we're OK to actually send the transactions
- return ptx_vector;
-
- }
- // only catch this here, other exceptions need to pass through to the calling function
- catch (const tools::error::tx_too_big& e)
- {
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- if (attempt_count >= MAX_SPLIT_ATTEMPTS)
- {
- throw;
- }
- }
- catch (...)
- {
- // in case of some other exception, make sure any tx in queue are marked unspent again
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- throw;
- }
+ if (m_transfers[n].amount() < fee_per_kb)
+ unmixable_dust_outputs.push_back(n);
+ else
+ unmixable_transfer_outputs.push_back(n);
}
+
+ return create_transactions_from(m_account_public_address, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), trusted_daemon);
}
bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 4cd74d4a2..4777102af 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -346,8 +346,6 @@ namespace tools
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon);
template<typename T>
- void transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
- template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count,
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::list<size_t> selected_transfers, size_t fake_outputs_count,
@@ -361,6 +359,7 @@ namespace tools
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
+ std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection(bool *same_version = NULL);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 8d5223006..0f622c26c 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -219,6 +219,20 @@ struct Wallet
*/
virtual void initAsync(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0;
+ /*!
+ * \brief setRefreshFromBlockHeight - start refresh from block height on recover
+ *
+ * \param refresh_from_block_height - blockchain start height
+ */
+ virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
+
+ /*!
+ * \brief setRecoveringFromSeed - set state recover form seed
+ *
+ * \param recoveringFromSeed - true/false
+ */
+ virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
+
/**
* @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed
* @return
@@ -255,6 +269,12 @@ struct Wallet
*/
virtual uint64_t daemonBlockChainTargetHeight() const = 0;
+ /**
+ * @brief synchronized - checks if wallet was ever synchronized
+ * @return
+ */
+ virtual bool synchronized() const = 0;
+
static std::string displayAmount(uint64_t amount);
static uint64_t amountFromString(const std::string &amount);
static uint64_t amountFromDouble(double amount);
@@ -347,9 +367,11 @@ struct WalletManager
* \brief recovers existing wallet using memo (electrum seed)
* \param path Name of wallet file to be created
* \param memo memo (25 words electrum seed)
+ * \param testnet testnet
+ * \param restoreHeight restore from start height
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
- virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false) = 0;
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0;
/*!
* \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted