mirror of
https://git.wownero.com/wownero/wownero.git
synced 2025-01-08 20:18:53 +00:00
commit
76c7878dea
@ -56,7 +56,7 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON)
|
||||
endif()
|
||||
|
||||
if(USE_DEVICE_TREZOR_UDP_RELEASE)
|
||||
add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1)
|
||||
add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1)
|
||||
endif()
|
||||
|
||||
if (Protobuf_INCLUDE_DIR)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=bdb
|
||||
$(package)_version=4.8.30
|
||||
$(package)_download_path=http://download.oracle.com/berkeley-db
|
||||
$(package)_download_path=https://download.oracle.com/berkeley-db
|
||||
$(package)_file_name=db-$($(package)_version).NC.tar.gz
|
||||
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
|
||||
$(package)_build_subdir=build_unix
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=ldns
|
||||
$(package)_version=1.6.17
|
||||
$(package)_download_path=http://www.nlnetlabs.nl/downloads/ldns/
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/ldns/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=8b88e059452118e8949a2752a55ce59bc71fa5bc414103e17f5b6b06f9bcc8cd
|
||||
$(package)_dependencies=openssl
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=libICE
|
||||
$(package)_version=1.0.9
|
||||
$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
|
||||
$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202
|
||||
$(package)_dependencies=xtrans xproto
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=libSM
|
||||
$(package)_version=1.2.2
|
||||
$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
|
||||
$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd
|
||||
$(package)_dependencies=xtrans xproto libICE
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=libusb
|
||||
$(package)_version=1.0.22
|
||||
$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/
|
||||
$(package)_download_path=https://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=native_cdrkit
|
||||
$(package)_version=1.1.11
|
||||
$(package)_download_path=http://distro.ibiblio.org/fatdog/source/600/c
|
||||
$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c
|
||||
$(package)_file_name=cdrkit-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564
|
||||
$(package)_patches=cdrkit-deterministic.patch
|
||||
|
@ -1,6 +1,6 @@
|
||||
PACKAGE=qt
|
||||
$(package)_version=5.7.1
|
||||
$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules
|
||||
$(package)_download_path=https://download.qt.io/archive/qt/5.7/5.7.1/submodules
|
||||
$(package)_suffix=opensource-src-$($(package)_version).tar.gz
|
||||
$(package)_file_name=qtbase-$($(package)_suffix)
|
||||
$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=unbound
|
||||
$(package)_version=1.6.8
|
||||
$(package)_download_path=http://www.unbound.net/downloads/
|
||||
$(package)_download_path=https://www.unbound.net/downloads/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=e3b428e33f56a45417107448418865fe08d58e0e7fea199b855515f60884dd49
|
||||
$(package)_dependencies=openssl expat ldns
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=unwind
|
||||
$(package)_version=1.2
|
||||
$(package)_download_path=http://download.savannah.nongnu.org/releases/libunwind
|
||||
$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind
|
||||
$(package)_file_name=lib$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=xproto
|
||||
$(package)_version=7.0.26
|
||||
$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto
|
||||
$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package=zlib
|
||||
$(package)_version=1.2.11
|
||||
$(package)_download_path=http://www.zlib.net
|
||||
$(package)_download_path=https://www.zlib.net
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
|
||||
|
@ -53,7 +53,6 @@ static size_t query_page_size()
|
||||
MERROR("Failed to determine page size");
|
||||
return 0;
|
||||
}
|
||||
MINFO("Page size: " << ret);
|
||||
return ret;
|
||||
#else
|
||||
#warning Missing query_page_size implementation
|
||||
|
@ -17,3 +17,12 @@
|
||||
fun:maybe_unlock_and_signal_one<boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex> >
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
we leak the logger, for performance reasons in on-the-fly init
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:_Znwm
|
||||
fun:_ZN2el4base7Storage7getELPPEv
|
||||
...
|
||||
}
|
||||
|
15
external/easylogging++/easylogging++.cc
vendored
15
external/easylogging++/easylogging++.cc
vendored
@ -2191,17 +2191,13 @@ void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs)
|
||||
# define ELPP_DEFAULT_LOGGING_FLAGS 0x0
|
||||
#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS)
|
||||
// Storage
|
||||
el::base::type::StoragePointer getresetELPP(bool reset)
|
||||
el::base::type::StoragePointer &el::base::Storage::getELPP()
|
||||
{
|
||||
static el::base::type::StoragePointer p(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())));
|
||||
if (reset)
|
||||
p = NULL;
|
||||
return p;
|
||||
}
|
||||
el::base::type::StoragePointer el::base::Storage::getELPP()
|
||||
{
|
||||
return getresetELPP(false);
|
||||
if (!el::base::elStorage)
|
||||
el::base::elStorage = new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()));
|
||||
return el::base::elStorage;
|
||||
}
|
||||
static struct EnsureELPP { EnsureELPP() { el::base::Storage::getELPP(); } } ensureELPP;
|
||||
#if ELPP_ASYNC_LOGGING
|
||||
Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
|
||||
#else
|
||||
@ -2250,7 +2246,6 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) :
|
||||
|
||||
Storage::~Storage(void) {
|
||||
ELPP_INTERNAL_INFO(4, "Destroying storage");
|
||||
getresetELPP(true);
|
||||
#if ELPP_ASYNC_LOGGING
|
||||
ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
|
||||
uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
|
||||
|
9
external/easylogging++/easylogging++.h
vendored
9
external/easylogging++/easylogging++.h
vendored
@ -552,7 +552,7 @@ typedef std::ostream ostream_t;
|
||||
typedef unsigned int EnumType;
|
||||
typedef unsigned short VerboseLevel;
|
||||
typedef unsigned long int LineNumber;
|
||||
typedef std::shared_ptr<base::Storage> StoragePointer;
|
||||
typedef base::Storage *StoragePointer;
|
||||
typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr;
|
||||
typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr;
|
||||
typedef std::shared_ptr<LoggerRegistrationCallback> LoggerRegistrationCallbackPtr;
|
||||
@ -2734,7 +2734,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static el::base::type::StoragePointer getELPP();
|
||||
static el::base::type::StoragePointer &getELPP();
|
||||
|
||||
private:
|
||||
base::RegisteredHitCounters* m_registeredHitCounters;
|
||||
@ -4613,9 +4613,10 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \
|
||||
}
|
||||
|
||||
#if ELPP_ASYNC_LOGGING
|
||||
# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL)
|
||||
# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\
|
||||
new el::base::AsyncDispatchWorker()))
|
||||
#else
|
||||
# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL)
|
||||
# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())))
|
||||
#endif // ELPP_ASYNC_LOGGING
|
||||
#define INITIALIZE_NULL_EASYLOGGINGPP \
|
||||
namespace el {\
|
||||
|
@ -1404,6 +1404,13 @@ public:
|
||||
*/
|
||||
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0;
|
||||
|
||||
/**
|
||||
* @brief prune output data for the given amount
|
||||
*
|
||||
* @param amount the amount for which to prune data
|
||||
*/
|
||||
virtual void prune_outputs(uint64_t amount) = 0;
|
||||
|
||||
/**
|
||||
* @brief runs a function over all txpool transactions
|
||||
*
|
||||
|
@ -1075,6 +1075,60 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in
|
||||
throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str()));
|
||||
}
|
||||
|
||||
void BlockchainLMDB::prune_outputs(uint64_t amount)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||
CURSOR(output_amounts);
|
||||
CURSOR(output_txs);
|
||||
|
||||
MINFO("Pruning outputs for amount " << amount);
|
||||
|
||||
MDB_val v;
|
||||
MDB_val_set(k, amount);
|
||||
int result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
return;
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error looking up outputs: ", result).c_str()));
|
||||
|
||||
// gather output ids
|
||||
mdb_size_t num_elems;
|
||||
mdb_cursor_count(m_cur_output_amounts, &num_elems);
|
||||
MINFO(num_elems << " outputs found");
|
||||
std::vector<uint64_t> output_ids;
|
||||
output_ids.reserve(num_elems);
|
||||
while (1)
|
||||
{
|
||||
const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
|
||||
output_ids.push_back(okp->output_id);
|
||||
MDEBUG("output id " << okp->output_id);
|
||||
result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP);
|
||||
if (result == MDB_NOTFOUND)
|
||||
break;
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error counting outputs: ", result).c_str()));
|
||||
}
|
||||
if (output_ids.size() != num_elems)
|
||||
throw0(DB_ERROR("Unexpected number of outputs"));
|
||||
|
||||
result = mdb_cursor_del(m_cur_output_amounts, MDB_NODUPDATA);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error deleting outputs: ", result).c_str()));
|
||||
|
||||
for (uint64_t output_id: output_ids)
|
||||
{
|
||||
MDB_val_set(v, output_id);
|
||||
result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error looking up output: ", result).c_str()));
|
||||
result = mdb_cursor_del(m_cur_output_txs, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error deleting output: ", result).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@ -2229,11 +2283,19 @@ uint64_t BlockchainLMDB::num_outputs() const
|
||||
TXN_PREFIX_RDONLY();
|
||||
int result;
|
||||
|
||||
// get current height
|
||||
MDB_stat db_stats;
|
||||
if ((result = mdb_stat(m_txn, m_output_txs, &db_stats)))
|
||||
RCURSOR(output_txs)
|
||||
|
||||
uint64_t num = 0;
|
||||
MDB_val k, v;
|
||||
result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_LAST);
|
||||
if (result == MDB_NOTFOUND)
|
||||
num = 0;
|
||||
else if (result == 0)
|
||||
num = 1 + ((const outtx*)v.mv_data)->output_id;
|
||||
else
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str()));
|
||||
return db_stats.ms_entries;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
|
||||
|
@ -345,6 +345,8 @@ private:
|
||||
|
||||
void remove_output(const uint64_t amount, const uint64_t& out_index);
|
||||
|
||||
virtual void prune_outputs(uint64_t amount);
|
||||
|
||||
virtual void add_spent_key(const crypto::key_image& k_image);
|
||||
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image);
|
||||
|
@ -81,6 +81,17 @@ monero_private_headers(blockchain_usage
|
||||
|
||||
|
||||
|
||||
set(blockchain_prune_known_spent_data_sources
|
||||
blockchain_prune_known_spent_data.cpp
|
||||
)
|
||||
|
||||
set(blockchain_prune_known_spent_data_private_headers)
|
||||
|
||||
monero_private_headers(blockchain_prune_known_spent_data
|
||||
${blockchain_prune_known_spent_data_private_headers})
|
||||
|
||||
|
||||
|
||||
set(blockchain_ancestry_sources
|
||||
blockchain_ancestry.cpp
|
||||
)
|
||||
@ -265,3 +276,25 @@ set_property(TARGET blockchain_stats
|
||||
PROPERTY
|
||||
OUTPUT_NAME "wownero-blockchain-stats")
|
||||
install(TARGETS blockchain_stats DESTINATION bin)
|
||||
|
||||
monero_add_executable(blockchain_prune_known_spent_data
|
||||
${blockchain_prune_known_spent_data_sources}
|
||||
${blockchain_prune_known_spent_data_private_headers})
|
||||
|
||||
target_link_libraries(blockchain_prune_known_spent_data
|
||||
PRIVATE
|
||||
cryptonote_core
|
||||
blockchain_db
|
||||
p2p
|
||||
version
|
||||
epee
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
set_property(TARGET blockchain_prune_known_spent_data
|
||||
PROPERTY
|
||||
OUTPUT_NAME "wownero-blockchain-prune-known-spent-data")
|
||||
install(TARGETS blockchain_prune_known_spent_data DESTINATION bin)
|
||||
|
@ -59,6 +59,7 @@ static MDB_dbi dbi_relative_rings;
|
||||
static MDB_dbi dbi_outputs;
|
||||
static MDB_dbi dbi_processed_txidx;
|
||||
static MDB_dbi dbi_spent;
|
||||
static MDB_dbi dbi_per_amount;
|
||||
static MDB_dbi dbi_ring_instances;
|
||||
static MDB_dbi dbi_stats;
|
||||
static MDB_env *env = NULL;
|
||||
@ -238,7 +239,7 @@ static void init(std::string cache_filename)
|
||||
|
||||
dbr = mdb_env_create(&env);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_env_set_maxdbs(env, 6);
|
||||
dbr = mdb_env_set_maxdbs(env, 7);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
|
||||
const std::string actual_filename = get_cache_filename(cache_filename);
|
||||
dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664);
|
||||
@ -265,6 +266,10 @@ static void init(std::string cache_filename)
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_dupsort(txn, dbi_spent, compare_uint64);
|
||||
|
||||
dbr = mdb_dbi_open(txn, "per_amount", MDB_CREATE | MDB_INTEGERKEY, &dbi_per_amount);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_compare(txn, dbi_per_amount, compare_uint64);
|
||||
|
||||
dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
@ -283,6 +288,7 @@ static void close()
|
||||
mdb_dbi_close(env, dbi_relative_rings);
|
||||
mdb_dbi_close(env, dbi_outputs);
|
||||
mdb_dbi_close(env, dbi_processed_txidx);
|
||||
mdb_dbi_close(env, dbi_per_amount);
|
||||
mdb_dbi_close(env, dbi_spent);
|
||||
mdb_dbi_close(env, dbi_ring_instances);
|
||||
mdb_dbi_close(env, dbi_stats);
|
||||
@ -585,6 +591,55 @@ static std::vector<output_data> get_spent_outputs(MDB_txn *txn)
|
||||
return outs;
|
||||
}
|
||||
|
||||
static void get_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t &total, uint64_t &spent)
|
||||
{
|
||||
MDB_cursor *cur;
|
||||
int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr)));
|
||||
MDB_val k, v;
|
||||
mdb_size_t count = 0;
|
||||
k.mv_size = sizeof(uint64_t);
|
||||
k.mv_data = (void*)&amount;
|
||||
dbr = mdb_cursor_get(cur, &k, &v, MDB_SET);
|
||||
if (dbr == MDB_NOTFOUND)
|
||||
{
|
||||
total = spent = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr)));
|
||||
total = ((const uint64_t*)v.mv_data)[0];
|
||||
spent = ((const uint64_t*)v.mv_data)[1];
|
||||
}
|
||||
mdb_cursor_close(cur);
|
||||
}
|
||||
|
||||
static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total, uint64_t spent)
|
||||
{
|
||||
MDB_cursor *cur;
|
||||
int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr)));
|
||||
MDB_val k, v;
|
||||
mdb_size_t count = 0;
|
||||
k.mv_size = sizeof(uint64_t);
|
||||
k.mv_data = (void*)&amount;
|
||||
dbr = mdb_cursor_get(cur, &k, &v, MDB_SET);
|
||||
if (dbr == 0)
|
||||
{
|
||||
total += ((const uint64_t*)v.mv_data)[0];
|
||||
spent += ((const uint64_t*)v.mv_data)[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(dbr == MDB_NOTFOUND, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr)));
|
||||
}
|
||||
uint64_t data[2] = {total, spent};
|
||||
v.mv_size = 2 * sizeof(uint64_t);
|
||||
v.mv_data = (void*)data;
|
||||
dbr = mdb_cursor_put(cur, &k, &v, 0);
|
||||
mdb_cursor_close(cur);
|
||||
}
|
||||
|
||||
static uint64_t get_processed_txidx(const std::string &name)
|
||||
{
|
||||
MDB_txn *txn;
|
||||
@ -1193,6 +1248,7 @@ int main(int argc, char* argv[])
|
||||
for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool
|
||||
{
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen);
|
||||
for (const auto &in: tx.vin)
|
||||
{
|
||||
if (in.type() != typeid(txin_to_key))
|
||||
@ -1210,6 +1266,9 @@ int main(int argc, char* argv[])
|
||||
std::vector<uint64_t> new_ring = canonicalize(txin.key_offsets);
|
||||
const uint32_t ring_size = txin.key_offsets.size();
|
||||
const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring);
|
||||
uint64_t pa_total = 0, pa_spent = 0;
|
||||
if (!opt_rct_only)
|
||||
get_per_amount_outputs(txn, txin.amount, pa_total, pa_spent);
|
||||
if (n == 0 && ring_size == 1)
|
||||
{
|
||||
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]);
|
||||
@ -1237,6 +1296,21 @@ int main(int argc, char* argv[])
|
||||
inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings");
|
||||
}
|
||||
}
|
||||
else if (n == 0 && !opt_rct_only && pa_spent + 1 == pa_total)
|
||||
{
|
||||
for (size_t o = 0; o < pa_total; ++o)
|
||||
{
|
||||
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, o);
|
||||
if (opt_verbose)
|
||||
{
|
||||
MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to as many outputs of that amount being spent as exist so far");
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
if (add_spent_output(cur, output_data(txin.amount, o)))
|
||||
inc_stat(txn, txin.amount ? "pre-rct-full-count" : "rct-full-count");
|
||||
}
|
||||
}
|
||||
else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size())
|
||||
{
|
||||
for (size_t o = 0; o < new_ring.size(); ++o)
|
||||
@ -1299,9 +1373,28 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
{
|
||||
set_relative_ring(txn, txin.k_image, new_ring);
|
||||
if (!opt_rct_only)
|
||||
inc_per_amount_outputs(txn, txin.amount, 0, 1);
|
||||
}
|
||||
}
|
||||
set_processed_txidx(txn, canonical, start_idx+1);
|
||||
if (!opt_rct_only)
|
||||
{
|
||||
for (const auto &out: tx.vout)
|
||||
{
|
||||
uint64_t amount = out.amount;
|
||||
if (miner_tx && tx.version >= 2)
|
||||
amount = 0;
|
||||
|
||||
if (opt_rct_only && amount != 0)
|
||||
continue;
|
||||
if (out.target.type() != typeid(txout_to_key))
|
||||
continue;
|
||||
inc_per_amount_outputs(txn, amount, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
++records;
|
||||
if (records >= records_per_sync)
|
||||
@ -1433,6 +1526,7 @@ skip_secondary_passes:
|
||||
{ "pre-rct-ring-size-1", pre_rct }, { "rct-ring-size-1", rct },
|
||||
{ "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct },
|
||||
{ "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct },
|
||||
{ "pre-rct-full-count", pre_rct }, { "rct-full-count", rct },
|
||||
{ "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct },
|
||||
{ "pre-rct-extra", pre_rct }, { "rct-ring-extra", rct },
|
||||
{ "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct },
|
||||
|
305
src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
Normal file
305
src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "common/command_line.h"
|
||||
#include "serialization/crypto.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_db/db_types.h"
|
||||
#include "version.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "bcutil"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
static std::map<uint64_t, uint64_t> load_outputs(const std::string &filename)
|
||||
{
|
||||
std::map<uint64_t, uint64_t> outputs;
|
||||
uint64_t amount = std::numeric_limits<uint64_t>::max();
|
||||
FILE *f;
|
||||
|
||||
f = fopen(filename.c_str(), "r");
|
||||
if (!f)
|
||||
{
|
||||
MERROR("Failed to load outputs from " << filename << ": " << strerror(errno));
|
||||
return {};
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
char s[256];
|
||||
if (!fgets(s, sizeof(s), f))
|
||||
break;
|
||||
if (feof(f))
|
||||
break;
|
||||
const size_t len = strlen(s);
|
||||
if (len > 0 && s[len - 1] == '\n')
|
||||
s[len - 1] = 0;
|
||||
if (!s[0])
|
||||
continue;
|
||||
std::pair<uint64_t, uint64_t> output;
|
||||
uint64_t offset, num_offsets;
|
||||
if (sscanf(s, "@%" PRIu64, &amount) == 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (amount == std::numeric_limits<uint64_t>::max())
|
||||
{
|
||||
MERROR("Bad format in " << filename);
|
||||
continue;
|
||||
}
|
||||
if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits<uint64_t>::max() - offset)
|
||||
{
|
||||
outputs[amount] += num_offsets;
|
||||
}
|
||||
else if (sscanf(s, "%" PRIu64, &offset) == 1)
|
||||
{
|
||||
outputs[amount] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MERROR("Bad format in " << filename);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return outputs;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
std::string default_db_type = "lmdb";
|
||||
|
||||
std::string available_dbs = cryptonote::blockchain_db_types(", ");
|
||||
available_dbs = "available: " + available_dbs;
|
||||
|
||||
uint32_t log_level = 0;
|
||||
|
||||
tools::on_startup();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options");
|
||||
po::options_description desc_cmd_sett("Command line options and settings options");
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<std::string> arg_database = {
|
||||
"database", available_dbs.c_str(), default_db_type
|
||||
};
|
||||
const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output", false};
|
||||
const command_line::arg_descriptor<bool> arg_dry_run = {"dry-run", "Do not actually prune", false};
|
||||
const command_line::arg_descriptor<std::string> arg_input = {"input", "Path to the known spent outputs file"};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_database);
|
||||
command_line::add_arg(desc_cmd_sett, arg_verbose);
|
||||
command_line::add_arg(desc_cmd_sett, arg_dry_run);
|
||||
command_line::add_arg(desc_cmd_sett, arg_input);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mlog_configure(mlog_get_default_log_path("wownero-blockchain-prune-known-spent-data.log"), true);
|
||||
if (!command_line::is_arg_defaulted(vm, arg_log_level))
|
||||
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
|
||||
else
|
||||
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
|
||||
|
||||
LOG_PRINT_L0("Starting...");
|
||||
|
||||
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
|
||||
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
|
||||
bool opt_verbose = command_line::get_arg(vm, arg_verbose);
|
||||
bool opt_dry_run = command_line::get_arg(vm, arg_dry_run);
|
||||
|
||||
std::string db_type = command_line::get_arg(vm, arg_database);
|
||||
if (!cryptonote::blockchain_valid_db_type(db_type))
|
||||
{
|
||||
std::cerr << "Invalid database type: " << db_type << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string input = command_line::get_arg(vm, arg_input);
|
||||
|
||||
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
|
||||
std::unique_ptr<Blockchain> core_storage;
|
||||
tx_memory_pool m_mempool(*core_storage);
|
||||
core_storage.reset(new Blockchain(m_mempool));
|
||||
BlockchainDB *db = new_db(db_type);
|
||||
if (db == NULL)
|
||||
{
|
||||
LOG_ERROR("Attempted to use non-existent database type: " << db_type);
|
||||
throw std::runtime_error("Attempting to use non-existent database type");
|
||||
}
|
||||
LOG_PRINT_L0("database: " << db_type);
|
||||
|
||||
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, 0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_PRINT_L0("Error opening database: " << e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
LOG_PRINT_L0("Source blockchain storage initialized OK");
|
||||
|
||||
std::map<uint64_t, uint64_t> known_spent_outputs;
|
||||
if (input.empty())
|
||||
{
|
||||
std::map<uint64_t, std::pair<uint64_t, uint64_t>> outputs;
|
||||
|
||||
LOG_PRINT_L0("Scanning for known spent data...");
|
||||
db->for_all_transactions([&](const crypto::hash &txid, const cryptonote::transaction &tx){
|
||||
const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen);
|
||||
for (const auto &in: tx.vin)
|
||||
{
|
||||
if (in.type() != typeid(txin_to_key))
|
||||
continue;
|
||||
const auto &txin = boost::get<txin_to_key>(in);
|
||||
if (txin.amount == 0)
|
||||
continue;
|
||||
|
||||
outputs[txin.amount].second++;
|
||||
}
|
||||
|
||||
for (const auto &out: tx.vout)
|
||||
{
|
||||
uint64_t amount = out.amount;
|
||||
if (miner_tx && tx.version >= 2)
|
||||
amount = 0;
|
||||
if (amount == 0)
|
||||
continue;
|
||||
if (out.target.type() != typeid(txout_to_key))
|
||||
continue;
|
||||
|
||||
outputs[amount].first++;
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
for (const auto &i: outputs)
|
||||
{
|
||||
known_spent_outputs[i.first] = i.second.second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_PRINT_L0("Loading known spent data...");
|
||||
known_spent_outputs = load_outputs(input);
|
||||
}
|
||||
|
||||
LOG_PRINT_L0("Pruning known spent data...");
|
||||
|
||||
bool stop_requested = false;
|
||||
tools::signal_handler::install([&stop_requested](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
|
||||
db->batch_start();
|
||||
|
||||
size_t num_total_outputs = 0, num_prunable_outputs = 0, num_known_spent_outputs = 0, num_eligible_outputs = 0, num_eligible_known_spent_outputs = 0;
|
||||
for (auto i = known_spent_outputs.begin(); i != known_spent_outputs.end(); ++i)
|
||||
{
|
||||
uint64_t num_outputs = db->get_num_outputs(i->first);
|
||||
num_total_outputs += num_outputs;
|
||||
num_known_spent_outputs += i->second;
|
||||
if (i->first == 0 || is_valid_decomposed_amount(i->first))
|
||||
{
|
||||
if (opt_verbose)
|
||||
MINFO("Ignoring output value " << i->first << ", with " << num_outputs << " outputs");
|
||||
continue;
|
||||
}
|
||||
num_eligible_outputs += num_outputs;
|
||||
num_eligible_known_spent_outputs += i->second;
|
||||
if (opt_verbose)
|
||||
MINFO(i->first << ": " << i->second << "/" << num_outputs);
|
||||
if (num_outputs > i->second)
|
||||
continue;
|
||||
if (num_outputs && num_outputs < i->second)
|
||||
{
|
||||
MERROR("More outputs are spent than known for amount " << i->first << ", not touching");
|
||||
continue;
|
||||
}
|
||||
if (opt_verbose)
|
||||
MINFO("Pruning data for " << num_outputs << " outputs");
|
||||
if (!opt_dry_run)
|
||||
db->prune_outputs(i->first);
|
||||
num_prunable_outputs += i->second;
|
||||
}
|
||||
|
||||
db->batch_stop();
|
||||
|
||||
MINFO("Total outputs: " << num_total_outputs);
|
||||
MINFO("Known spent outputs: " << num_known_spent_outputs);
|
||||
MINFO("Eligible outputs: " << num_eligible_outputs);
|
||||
MINFO("Eligible known spent outputs: " << num_eligible_known_spent_outputs);
|
||||
MINFO("Prunable outputs: " << num_prunable_outputs);
|
||||
|
||||
LOG_PRINT_L0("Blockchain known spent data pruned OK");
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Error", 1);
|
||||
}
|
@ -175,7 +175,15 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
|
||||
public:
|
||||
transaction_prefix(){}
|
||||
transaction_prefix(){ set_null(); }
|
||||
void set_null()
|
||||
{
|
||||
version = 1;
|
||||
unlock_time = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
extra.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class transaction: public transaction_prefix
|
||||
@ -302,17 +310,12 @@ namespace cryptonote
|
||||
inline
|
||||
transaction::~transaction()
|
||||
{
|
||||
//set_null();
|
||||
}
|
||||
|
||||
inline
|
||||
void transaction::set_null()
|
||||
{
|
||||
version = 1;
|
||||
unlock_time = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
extra.clear();
|
||||
transaction_prefix::set_null();
|
||||
signatures.clear();
|
||||
rct_signatures.type = rct::RCTTypeNull;
|
||||
set_hash_valid(false);
|
||||
|
@ -535,6 +535,38 @@ bool Blockchain::deinit()
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function removes blocks from the top of blockchain.
|
||||
// It starts a batch and calls private method pop_block_from_blockchain().
|
||||
void Blockchain::pop_blocks(uint64_t nblocks)
|
||||
{
|
||||
uint64_t i;
|
||||
CRITICAL_REGION_LOCAL(m_tx_pool);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
|
||||
|
||||
while (!m_db->batch_start())
|
||||
{
|
||||
m_blockchain_lock.unlock();
|
||||
m_tx_pool.unlock();
|
||||
epee::misc_utils::sleep_no_w(1000);
|
||||
m_tx_pool.lock();
|
||||
m_blockchain_lock.lock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (i=0; i < nblocks; ++i)
|
||||
{
|
||||
pop_block_from_blockchain();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what());
|
||||
}
|
||||
|
||||
m_db->batch_stop();
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function tells BlockchainDB to remove the top block from the
|
||||
// blockchain and then returns all transactions (except the miner tx, of course)
|
||||
// from it to the tx_pool
|
||||
@ -3732,7 +3764,7 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
|
||||
void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
|
||||
{
|
||||
TIME_MEASURE_START(t);
|
||||
slow_hash_allocate_state();
|
||||
@ -3818,11 +3850,33 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) const
|
||||
void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const
|
||||
{
|
||||
try
|
||||
{
|
||||
m_db->get_output_key(epee::span<const uint64_t>(&amount, 1), offsets, outputs, true);
|
||||
if (outputs.size() < offsets.size())
|
||||
{
|
||||
const uint64_t n_outputs = m_db->get_num_outputs(amount);
|
||||
for (size_t i = outputs.size(); i < offsets.size(); ++i)
|
||||
{
|
||||
uint64_t idx = offsets[i];
|
||||
if (idx < n_outputs)
|
||||
{
|
||||
MWARNING("Index " << idx << " not found in db for amount " << amount << ", but it is less than the number of entries");
|
||||
break;
|
||||
}
|
||||
else if (idx < n_outputs + extra_tx_map.size())
|
||||
{
|
||||
outputs.push_back(extra_tx_map[idx - n_outputs]);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWARNING("missed " << amount << "/" << idx << " in " << extra_tx_map.size() << " (chain " << n_outputs << ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@ -3937,6 +3991,34 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
|
||||
// vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries
|
||||
// and is threaded if possible. The table (m_scan_table) will be used later when querying output
|
||||
// keys.
|
||||
static bool update_output_map(std::map<uint64_t, std::vector<output_data_t>> &extra_tx_map, const transaction &tx, uint64_t height, bool miner)
|
||||
{
|
||||
MTRACE("Blockchain::" << __func__);
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
const auto &out = tx.vout[i];
|
||||
if (out.target.type() != typeid(txout_to_key))
|
||||
continue;
|
||||
const txout_to_key &out_to_key = boost::get<txout_to_key>(out.target);
|
||||
rct::key commitment;
|
||||
uint64_t amount = out.amount;
|
||||
if (miner && tx.version == 2)
|
||||
{
|
||||
commitment = rct::zeroCommit(amount);
|
||||
amount = 0;
|
||||
}
|
||||
else if (tx.version > 1)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(i < tx.rct_signatures.outPk.size(), false, "Invalid outPk size");
|
||||
commitment = tx.rct_signatures.outPk[i].mask;
|
||||
}
|
||||
else
|
||||
commitment = rct::zero();
|
||||
extra_tx_map[amount].push_back(output_data_t{out_to_key.key, tx.unlock_time, height, commitment});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry)
|
||||
{
|
||||
MTRACE("Blockchain::" << __func__);
|
||||
@ -3983,42 +4065,40 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
m_blockchain_lock.lock();
|
||||
}
|
||||
|
||||
if ((m_db->height() + blocks_entry.size()) < m_blocks_hash_check.size())
|
||||
const uint64_t height = m_db->height();
|
||||
if ((height + blocks_entry.size()) < m_blocks_hash_check.size())
|
||||
return true;
|
||||
|
||||
bool blocks_exist = false;
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
uint64_t threads = tpool.get_max_concurrency();
|
||||
unsigned threads = tpool.get_max_concurrency();
|
||||
std::vector<block> blocks;
|
||||
blocks.resize(blocks_entry.size());
|
||||
|
||||
if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1)
|
||||
if (1)
|
||||
{
|
||||
// limit threads, default limit = 4
|
||||
if(threads > m_max_prepare_blocks_threads)
|
||||
threads = m_max_prepare_blocks_threads;
|
||||
|
||||
uint64_t height = m_db->height();
|
||||
int batches = blocks_entry.size() / threads;
|
||||
int extra = blocks_entry.size() % threads;
|
||||
unsigned int batches = blocks_entry.size() / threads;
|
||||
unsigned int extra = blocks_entry.size() % threads;
|
||||
MDEBUG("block_batches: " << batches);
|
||||
std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads);
|
||||
std::vector < std::vector < block >> blocks(threads);
|
||||
auto it = blocks_entry.begin();
|
||||
unsigned blockidx = 0;
|
||||
|
||||
for (uint64_t i = 0; i < threads; i++)
|
||||
for (unsigned i = 0; i < threads; i++)
|
||||
{
|
||||
blocks[i].reserve(batches + 1);
|
||||
for (int j = 0; j < batches; j++)
|
||||
for (unsigned int j = 0; j < batches; j++, ++blockidx)
|
||||
{
|
||||
block block;
|
||||
block &block = blocks[blockidx];
|
||||
|
||||
if (!parse_and_validate_block_from_blob(it->block, block))
|
||||
{
|
||||
std::advance(it, 1);
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
|
||||
// check first block and skip all blocks if its not chained properly
|
||||
if (i == 0 && j == 0)
|
||||
if (blockidx == 0)
|
||||
{
|
||||
crypto::hash tophash = m_db->top_block_hash();
|
||||
if (block.prev_id != tophash)
|
||||
@ -4033,20 +4113,16 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
break;
|
||||
}
|
||||
|
||||
blocks[i].push_back(std::move(block));
|
||||
std::advance(it, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < extra && !blocks_exist; i++)
|
||||
for (unsigned i = 0; i < extra && !blocks_exist; i++, blockidx++)
|
||||
{
|
||||
block block;
|
||||
block &block = blocks[blockidx];
|
||||
|
||||
if (!parse_and_validate_block_from_blob(it->block, block))
|
||||
{
|
||||
std::advance(it, 1);
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (have_block(get_block_hash(block)))
|
||||
{
|
||||
@ -4054,7 +4130,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
break;
|
||||
}
|
||||
|
||||
blocks[i].push_back(std::move(block));
|
||||
std::advance(it, 1);
|
||||
}
|
||||
|
||||
@ -4063,10 +4138,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
m_blocks_longhash_table.clear();
|
||||
uint64_t thread_height = height;
|
||||
tools::threadpool::waiter waiter;
|
||||
for (uint64_t i = 0; i < threads; i++)
|
||||
for (unsigned int i = 0; i < threads; i++)
|
||||
{
|
||||
tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])), true);
|
||||
thread_height += blocks[i].size();
|
||||
unsigned nblocks = batches;
|
||||
if (i < extra)
|
||||
++nblocks;
|
||||
tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, epee::span<const block>(&blocks[i], nblocks), std::ref(maps[i])), true);
|
||||
thread_height += nblocks;
|
||||
}
|
||||
|
||||
waiter.wait(&tpool);
|
||||
@ -4109,7 +4187,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
// [input] stores all absolute_offsets for each amount
|
||||
std::map<uint64_t, std::vector<uint64_t>> offset_map;
|
||||
// [output] stores all output_data_t for each absolute_offset
|
||||
std::map<uint64_t, std::vector<output_data_t>> tx_map;
|
||||
std::map<uint64_t, std::vector<output_data_t>> tx_map, extra_tx_map;
|
||||
std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
|
||||
|
||||
#define SCAN_TABLE_QUIT(m) \
|
||||
@ -4120,12 +4198,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
} while(0); \
|
||||
|
||||
// generate sorted tables for all amounts and absolute offsets
|
||||
size_t tx_index = 0;
|
||||
size_t tx_index = 0, block_index = 0;
|
||||
for (const auto &entry : blocks_entry)
|
||||
{
|
||||
if (m_cancel)
|
||||
return false;
|
||||
|
||||
if (!update_output_map(extra_tx_map, blocks[block_index].miner_tx, height + block_index, true))
|
||||
SCAN_TABLE_QUIT("Error building extra tx map.");
|
||||
for (const auto &tx_blob : entry.txs)
|
||||
{
|
||||
if (tx_index >= txes.size())
|
||||
@ -4184,7 +4264,10 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
offset_map[in_to_key.amount].push_back(offset);
|
||||
|
||||
}
|
||||
if (!update_output_map(extra_tx_map, tx, height + block_index, false))
|
||||
SCAN_TABLE_QUIT("Error building extra tx map.");
|
||||
}
|
||||
++block_index;
|
||||
}
|
||||
|
||||
// sort and remove duplicate absolute_offsets in offset_map
|
||||
@ -4207,7 +4290,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
for (size_t i = 0; i < amounts.size(); i++)
|
||||
{
|
||||
uint64_t amount = amounts[i];
|
||||
tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount])), true);
|
||||
tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::cref(extra_tx_map[amount])), true);
|
||||
}
|
||||
waiter.wait(&tpool);
|
||||
}
|
||||
@ -4216,7 +4299,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
for (size_t i = 0; i < amounts.size(); i++)
|
||||
{
|
||||
uint64_t amount = amounts[i];
|
||||
output_scan_worker(amount, offset_map[amount], tx_map[amount]);
|
||||
output_scan_worker(amount, offset_map[amount], tx_map[amount], extra_tx_map[amount]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,7 +918,7 @@ namespace cryptonote
|
||||
* @param outputs return-by-reference the outputs collected
|
||||
*/
|
||||
void output_scan_worker(const uint64_t amount,const std::vector<uint64_t> &offsets,
|
||||
std::vector<output_data_t> &outputs) const;
|
||||
std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const;
|
||||
|
||||
/**
|
||||
* @brief computes the "short" and "long" hashes for a set of blocks
|
||||
@ -927,7 +927,7 @@ namespace cryptonote
|
||||
* @param blocks the blocks to be hashed
|
||||
* @param map return-by-reference the hashes for each block
|
||||
*/
|
||||
void block_longhash_worker(uint64_t height, const std::vector<block> &blocks,
|
||||
void block_longhash_worker(uint64_t height, const epee::span<const block> &blocks,
|
||||
std::unordered_map<crypto::hash, crypto::hash> &map) const;
|
||||
|
||||
/**
|
||||
@ -967,6 +967,13 @@ namespace cryptonote
|
||||
*/
|
||||
std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
|
||||
|
||||
/**
|
||||
* @brief removes blocks from the top of the blockchain
|
||||
*
|
||||
* @param nblocks number of blocks to be removed
|
||||
*/
|
||||
void pop_blocks(uint64_t nblocks);
|
||||
|
||||
private:
|
||||
|
||||
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
|
||||
|
@ -160,6 +160,11 @@ namespace cryptonote
|
||||
, "Relay blocks as normal blocks"
|
||||
, false
|
||||
};
|
||||
static const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
||||
"pad-transactions"
|
||||
, "Pad relayed transactions to help defend against traffic volume analysis"
|
||||
, false
|
||||
};
|
||||
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
|
||||
"max-txpool-weight"
|
||||
, "Set maximum txpool weight in bytes."
|
||||
@ -185,7 +190,8 @@ namespace cryptonote
|
||||
m_disable_dns_checkpoints(false),
|
||||
m_update_download(0),
|
||||
m_nettype(UNDEFINED),
|
||||
m_update_available(false)
|
||||
m_update_available(false),
|
||||
m_pad_transactions(false)
|
||||
{
|
||||
m_checkpoints_updating.clear();
|
||||
set_cryptonote_protocol(pprotocol);
|
||||
@ -244,6 +250,7 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------
|
||||
void core::stop()
|
||||
{
|
||||
m_miner.stop();
|
||||
m_blockchain_storage.cancel();
|
||||
|
||||
tools::download_async_handle handle;
|
||||
@ -279,6 +286,7 @@ namespace cryptonote
|
||||
command_line::add_arg(desc, arg_offline);
|
||||
command_line::add_arg(desc, arg_disable_dns_checkpoints);
|
||||
command_line::add_arg(desc, arg_max_txpool_weight);
|
||||
command_line::add_arg(desc, arg_pad_transactions);
|
||||
command_line::add_arg(desc, arg_block_notify);
|
||||
|
||||
miner::init_options(desc);
|
||||
@ -317,6 +325,7 @@ namespace cryptonote
|
||||
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
|
||||
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
|
||||
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
|
||||
m_pad_transactions = get_arg(vm, arg_pad_transactions);
|
||||
m_offline = get_arg(vm, arg_offline);
|
||||
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
|
||||
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
|
||||
@ -893,13 +902,15 @@ namespace cryptonote
|
||||
bool ok = true;
|
||||
it = tx_blobs.begin();
|
||||
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
|
||||
if (already_have[i])
|
||||
continue;
|
||||
if (!results[i].res)
|
||||
{
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
if (keeped_by_block)
|
||||
get_blockchain_storage().on_new_tx_from_block(results[i].tx);
|
||||
if (already_have[i])
|
||||
continue;
|
||||
|
||||
const size_t weight = get_transaction_weight(results[i].tx, it->size());
|
||||
ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay);
|
||||
@ -1130,9 +1141,6 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
|
||||
{
|
||||
if (keeped_by_block)
|
||||
get_blockchain_storage().on_new_tx_from_block(tx);
|
||||
|
||||
if(m_mempool.have_tx(tx_hash))
|
||||
{
|
||||
LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
|
||||
|
@ -754,6 +754,13 @@ namespace cryptonote
|
||||
*/
|
||||
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
|
||||
|
||||
/**
|
||||
* @brief get whether transaction relay should be padded
|
||||
*
|
||||
* @return whether transaction relay should be padded
|
||||
*/
|
||||
bool pad_transactions() const { return m_pad_transactions; }
|
||||
|
||||
/**
|
||||
* @brief check a set of hashes against the precompiled hash set
|
||||
*
|
||||
@ -1013,6 +1020,7 @@ namespace cryptonote
|
||||
|
||||
bool m_fluffy_blocks_enabled;
|
||||
bool m_offline;
|
||||
bool m_pad_transactions;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -146,9 +146,11 @@ namespace cryptonote
|
||||
struct request
|
||||
{
|
||||
std::vector<blobdata> txs;
|
||||
std::string _; // padding
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(txs)
|
||||
KV_SERIALIZE(_)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -1726,8 +1726,39 @@ skip:
|
||||
bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)
|
||||
{
|
||||
// no check for success, so tell core they're relayed unconditionally
|
||||
const bool pad_transactions = m_core.pad_transactions();
|
||||
size_t bytes = pad_transactions ? 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(arg.txs.size()).size() : 0;
|
||||
for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end(); ++tx_blob_it)
|
||||
{
|
||||
m_core.on_transaction_relayed(*tx_blob_it);
|
||||
if (pad_transactions)
|
||||
bytes += tools::get_varint_data(tx_blob_it->size()).size() + tx_blob_it->size();
|
||||
}
|
||||
|
||||
if (pad_transactions)
|
||||
{
|
||||
// stuff some dummy bytes in to stay safe from traffic volume analysis
|
||||
static constexpr size_t granularity = 1024;
|
||||
size_t padding = granularity - bytes % granularity;
|
||||
const size_t overhead = 2 /* 1 + '_' */ + tools::get_varint_data(padding).size();
|
||||
if (overhead > padding)
|
||||
padding = 0;
|
||||
else
|
||||
padding -= overhead;
|
||||
arg._ = std::string(padding, ' ');
|
||||
|
||||
std::string arg_buff;
|
||||
epee::serialization::store_t_to_binary(arg, arg_buff);
|
||||
|
||||
// we probably lowballed the payload size a bit, so added a but too much. Fix this now.
|
||||
size_t remove = arg_buff.size() % granularity;
|
||||
if (remove > arg._.size())
|
||||
arg._.clear();
|
||||
else
|
||||
arg._.resize(arg._.size() - remove);
|
||||
// if the size of _ moved enough, we might lose byte in size encoding, we don't care
|
||||
}
|
||||
|
||||
return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -674,6 +674,31 @@ bool t_command_parser_executor::sync_info(const std::vector<std::string>& args)
|
||||
return m_executor.sync_info();
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() != 1)
|
||||
{
|
||||
std::cout << "Exactly one parameter is needed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
uint64_t nblocks = boost::lexical_cast<uint64_t>(args[0]);
|
||||
if (nblocks < 1)
|
||||
{
|
||||
std::cout << "number of blocks must be greater than 0" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return m_executor.pop_blocks(nblocks);
|
||||
}
|
||||
catch (const boost::bad_lexical_cast&)
|
||||
{
|
||||
std::cout << "number of blocks must be a number greater than 0" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::version(const std::vector<std::string>& args)
|
||||
{
|
||||
std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl;
|
||||
|
@ -139,6 +139,8 @@ public:
|
||||
|
||||
bool sync_info(const std::vector<std::string>& args);
|
||||
|
||||
bool pop_blocks(const std::vector<std::string>& args);
|
||||
|
||||
bool version(const std::vector<std::string>& args);
|
||||
};
|
||||
|
||||
|
@ -281,6 +281,12 @@ t_command_server::t_command_server(
|
||||
, std::bind(&t_command_parser_executor::sync_info, &m_parser, p::_1)
|
||||
, "Print information about the blockchain sync state."
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"pop_blocks"
|
||||
, std::bind(&t_command_parser_executor::pop_blocks, &m_parser, p::_1)
|
||||
, "pop_blocks <nblocks>"
|
||||
, "Remove blocks from end of blockchain"
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"version"
|
||||
, std::bind(&t_command_parser_executor::version, &m_parser, p::_1)
|
||||
|
@ -195,7 +195,6 @@ bool t_daemon::run(bool interactive)
|
||||
|
||||
for(auto& rpc : mp_internals->rpcs)
|
||||
rpc->stop();
|
||||
mp_internals->core.get().get_miner().stop();
|
||||
MGINFO("Node stopped.");
|
||||
return true;
|
||||
}
|
||||
@ -217,7 +216,6 @@ void t_daemon::stop()
|
||||
{
|
||||
throw std::runtime_error{"Can't stop stopped daemon"};
|
||||
}
|
||||
mp_internals->core.get().get_miner().stop();
|
||||
mp_internals->p2p.stop();
|
||||
for(auto& rpc : mp_internals->rpcs)
|
||||
rpc->stop();
|
||||
|
@ -216,6 +216,16 @@ int main(int argc, char const * argv[])
|
||||
// after logs initialized
|
||||
tools::create_directories_if_necessary(data_dir.string());
|
||||
|
||||
#ifdef STACK_TRACE
|
||||
tools::set_stack_trace_log(log_file_path.filename().string());
|
||||
#endif // STACK_TRACE
|
||||
|
||||
if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency))
|
||||
tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency));
|
||||
|
||||
// logging is now set up
|
||||
MGINFO("Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")");
|
||||
|
||||
// If there are positional options, we're running a daemon command
|
||||
{
|
||||
auto command = command_line::get_arg(vm, daemon_args::arg_command);
|
||||
@ -276,16 +286,6 @@ int main(int argc, char const * argv[])
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef STACK_TRACE
|
||||
tools::set_stack_trace_log(log_file_path.filename().string());
|
||||
#endif // STACK_TRACE
|
||||
|
||||
if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency))
|
||||
tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency));
|
||||
|
||||
// logging is now set up
|
||||
MGINFO("Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")");
|
||||
|
||||
MINFO("Moving from main() into the daemonize now.");
|
||||
|
||||
return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm) ? 0 : 1;
|
||||
|
@ -1967,4 +1967,31 @@ bool t_rpc_command_executor::sync_info()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::pop_blocks(uint64_t num_blocks)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_POP_BLOCKS::request req;
|
||||
cryptonote::COMMAND_RPC_POP_BLOCKS::response res;
|
||||
std::string fail_message = "pop_blocks failed";
|
||||
|
||||
req.nblocks = num_blocks;
|
||||
if (m_is_rpc)
|
||||
{
|
||||
if (!m_rpc_client->rpc_request(req, res, "/pop_blocks", fail_message.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_rpc_server->on_pop_blocks(req, res) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
tools::success_msg_writer() << "new height: " << res.height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}// namespace daemonize
|
||||
|
@ -152,6 +152,8 @@ public:
|
||||
bool relay_tx(const std::string &txid);
|
||||
|
||||
bool sync_info();
|
||||
|
||||
bool pop_blocks(uint64_t num_blocks);
|
||||
};
|
||||
|
||||
} // namespace daemonize
|
||||
|
@ -80,6 +80,14 @@ namespace hw {
|
||||
return false;
|
||||
}
|
||||
|
||||
class i_device_callback {
|
||||
public:
|
||||
virtual void on_button_request() {}
|
||||
virtual void on_pin_request(epee::wipeable_string & pin) {}
|
||||
virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}
|
||||
virtual ~i_device_callback() = default;
|
||||
};
|
||||
|
||||
class device {
|
||||
protected:
|
||||
std::string name;
|
||||
@ -129,6 +137,8 @@ namespace hw {
|
||||
virtual device_type get_type() const = 0;
|
||||
|
||||
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
|
||||
virtual void set_callback(i_device_callback * callback) {};
|
||||
virtual void set_derivation_path(const std::string &derivation_path) {};
|
||||
|
||||
/* ======================================================================= */
|
||||
/* LOCKER */
|
||||
|
@ -121,7 +121,8 @@ namespace trezor {
|
||||
const boost::optional<cryptonote::network_type> & network_type){
|
||||
AUTO_LOCK_CMD();
|
||||
require_connected();
|
||||
test_ping();
|
||||
device_state_reset_unsafe();
|
||||
require_initialized();
|
||||
|
||||
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
|
||||
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
|
||||
@ -136,7 +137,8 @@ namespace trezor {
|
||||
const boost::optional<cryptonote::network_type> & network_type){
|
||||
AUTO_LOCK_CMD();
|
||||
require_connected();
|
||||
test_ping();
|
||||
device_state_reset_unsafe();
|
||||
require_initialized();
|
||||
|
||||
auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
|
||||
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
|
||||
@ -152,7 +154,8 @@ namespace trezor {
|
||||
{
|
||||
AUTO_LOCK_CMD();
|
||||
require_connected();
|
||||
test_ping();
|
||||
device_state_reset_unsafe();
|
||||
require_initialized();
|
||||
|
||||
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
|
||||
|
||||
@ -238,12 +241,11 @@ namespace trezor {
|
||||
cpend.construction_data = cdata.tx_data;
|
||||
|
||||
// Transaction check
|
||||
cryptonote::blobdata tx_blob;
|
||||
cryptonote::transaction tx_deserialized;
|
||||
bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
|
||||
r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
|
||||
try {
|
||||
transaction_check(cdata, aux_data);
|
||||
} catch(const std::exception &e){
|
||||
throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what());
|
||||
}
|
||||
|
||||
std::string key_images;
|
||||
bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool
|
||||
@ -283,7 +285,8 @@ namespace trezor {
|
||||
{
|
||||
AUTO_LOCK_CMD();
|
||||
require_connected();
|
||||
test_ping();
|
||||
device_state_reset_unsafe();
|
||||
require_initialized();
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
|
||||
signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
|
||||
@ -294,6 +297,7 @@ namespace trezor {
|
||||
// Step: Init
|
||||
auto init_msg = signer->step_init();
|
||||
this->set_msg_addr(init_msg.get());
|
||||
transaction_pre_check(init_msg);
|
||||
|
||||
auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
|
||||
signer->step_init_ack(response);
|
||||
@ -351,6 +355,59 @@ namespace trezor {
|
||||
signer->step_final_ack(ack_final);
|
||||
}
|
||||
|
||||
void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty");
|
||||
CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data");
|
||||
CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features
|
||||
const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0;
|
||||
|
||||
if (nonce_required){
|
||||
// Versions 2.0.9 and lower do not support payment ID
|
||||
CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information");
|
||||
const uint32_t vma = m_features->major_version();
|
||||
const uint32_t vmi = m_features->minor_version();
|
||||
const uint32_t vpa = m_features->patch_version();
|
||||
if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) {
|
||||
throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data)
|
||||
{
|
||||
// Simple serialization check
|
||||
cryptonote::blobdata tx_blob;
|
||||
cryptonote::transaction tx_deserialized;
|
||||
bool r = cryptonote::t_serializable_object_to_blob(tdata.tx, tx_blob);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
|
||||
r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
|
||||
|
||||
// Extras check
|
||||
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
|
||||
cryptonote::tx_extra_nonce nonce;
|
||||
|
||||
r = cryptonote::parse_tx_extra(tdata.tx.extra, tx_extra_fields);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "tx.extra parsing failed");
|
||||
|
||||
const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0;
|
||||
const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce);
|
||||
CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent");
|
||||
|
||||
if (nonce_required){
|
||||
const std::string & payment_id = tdata.tsx_data.payment_id();
|
||||
if (payment_id.size() == 32){
|
||||
crypto::hash payment_id_long{};
|
||||
CHECK_AND_ASSERT_THROW_MES(cryptonote::get_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_long), "Long payment ID not present");
|
||||
|
||||
} else if (payment_id.size() == 8){
|
||||
crypto::hash8 payment_id_short{};
|
||||
CHECK_AND_ASSERT_THROW_MES(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_short), "Short payment ID not present");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else //WITH_DEVICE_TREZOR
|
||||
|
||||
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry) {
|
||||
|
@ -57,9 +57,8 @@ namespace trezor {
|
||||
*/
|
||||
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
|
||||
protected:
|
||||
// To speed up blockchain parsing the view key maybe handle here.
|
||||
crypto::secret_key viewkey;
|
||||
bool has_view_key;
|
||||
void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg);
|
||||
void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data);
|
||||
|
||||
public:
|
||||
device_trezor();
|
||||
|
@ -28,6 +28,9 @@
|
||||
//
|
||||
|
||||
#include "device_trezor_base.hpp"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace hw {
|
||||
namespace trezor {
|
||||
@ -36,10 +39,11 @@ namespace trezor {
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
||||
#define TREZOR_BIP44_HARDENED_ZERO 0x80000000
|
||||
|
||||
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
|
||||
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080};
|
||||
|
||||
device_trezor_base::device_trezor_base() {
|
||||
device_trezor_base::device_trezor_base(): m_callback(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
@ -61,7 +65,7 @@ namespace trezor {
|
||||
}
|
||||
|
||||
bool device_trezor_base::set_name(const std::string & name) {
|
||||
this->full_name = name;
|
||||
this->m_full_name = name;
|
||||
this->name = "";
|
||||
|
||||
auto delim = name.find(':');
|
||||
@ -73,10 +77,10 @@ namespace trezor {
|
||||
}
|
||||
|
||||
const std::string device_trezor_base::get_name() const {
|
||||
if (this->full_name.empty()) {
|
||||
if (this->m_full_name.empty()) {
|
||||
return std::string("<disconnected:").append(this->name).append(">");
|
||||
}
|
||||
return this->full_name;
|
||||
return this->m_full_name;
|
||||
}
|
||||
|
||||
bool device_trezor_base::init() {
|
||||
@ -135,6 +139,9 @@ namespace trezor {
|
||||
}
|
||||
|
||||
bool device_trezor_base::disconnect() {
|
||||
m_device_state.clear();
|
||||
m_features.reset();
|
||||
|
||||
if (m_transport){
|
||||
try {
|
||||
m_transport->close();
|
||||
@ -189,6 +196,25 @@ namespace trezor {
|
||||
}
|
||||
}
|
||||
|
||||
void device_trezor_base::require_initialized(){
|
||||
if (!m_features){
|
||||
throw exc::TrezorException("Device state not initialized");
|
||||
}
|
||||
|
||||
if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){
|
||||
throw exc::TrezorException("Device is in the bootloader mode");
|
||||
}
|
||||
|
||||
if (m_features->has_firmware_present() && !m_features->firmware_present()){
|
||||
throw exc::TrezorException("Device has no firmware loaded");
|
||||
}
|
||||
|
||||
// Hard requirement on initialized field, has to be there.
|
||||
if (!m_features->has_initialized() || !m_features->initialized()){
|
||||
throw exc::TrezorException("Device is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
void device_trezor_base::call_ping_unsafe(){
|
||||
auto pingMsg = std::make_shared<messages::management::Ping>();
|
||||
pingMsg->set_message("PING");
|
||||
@ -213,7 +239,7 @@ namespace trezor {
|
||||
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
|
||||
require_connected();
|
||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||
this->getTransport()->write(*msg);
|
||||
this->get_transport()->write(*msg);
|
||||
}
|
||||
|
||||
GenericMessage device_trezor_base::read_raw(){
|
||||
@ -221,7 +247,7 @@ namespace trezor {
|
||||
std::shared_ptr<google::protobuf::Message> msg_resp;
|
||||
hw::trezor::messages::MessageType msg_resp_type;
|
||||
|
||||
this->getTransport()->read(msg_resp, &msg_resp_type);
|
||||
this->get_transport()->read(msg_resp, &msg_resp_type);
|
||||
return GenericMessage(msg_resp_type, msg_resp);
|
||||
}
|
||||
|
||||
@ -252,6 +278,39 @@ namespace trezor {
|
||||
}
|
||||
}
|
||||
|
||||
void device_trezor_base::ensure_derivation_path() noexcept {
|
||||
if (m_wallet_deriv_path.empty()){
|
||||
m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO); // default 0'
|
||||
}
|
||||
}
|
||||
|
||||
void device_trezor_base::set_derivation_path(const std::string &deriv_path){
|
||||
this->m_wallet_deriv_path.clear();
|
||||
|
||||
if (deriv_path.empty() || deriv_path == "-"){
|
||||
ensure_derivation_path();
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(deriv_path.size() <= 255, "Derivation path is too long");
|
||||
|
||||
std::vector<std::string> fields;
|
||||
boost::split(fields, deriv_path, boost::is_any_of("/"));
|
||||
CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long");
|
||||
|
||||
boost::regex rgx("^([0-9]+)'?$");
|
||||
boost::cmatch match;
|
||||
|
||||
this->m_wallet_deriv_path.reserve(fields.size());
|
||||
for(const std::string & cur : fields){
|
||||
const bool ok = boost::regex_match(cur.c_str(), match, rgx);
|
||||
CHECK_AND_ASSERT_THROW_MES(ok, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);
|
||||
CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);
|
||||
|
||||
const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO;
|
||||
this->m_wallet_deriv_path.push_back((unsigned int)cidx);
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
/* TREZOR PROTOCOL */
|
||||
@ -277,6 +336,25 @@ namespace trezor {
|
||||
return false;
|
||||
}
|
||||
|
||||
void device_trezor_base::device_state_reset_unsafe()
|
||||
{
|
||||
require_connected();
|
||||
auto initMsg = std::make_shared<messages::management::Initialize>();
|
||||
|
||||
if(!m_device_state.empty()) {
|
||||
initMsg->set_allocated_state(&m_device_state);
|
||||
}
|
||||
|
||||
m_features = this->client_exchange<messages::management::Features>(initMsg);
|
||||
initMsg->release_state();
|
||||
}
|
||||
|
||||
void device_trezor_base::device_state_reset()
|
||||
{
|
||||
AUTO_LOCK_CMD();
|
||||
device_state_reset_unsafe();
|
||||
}
|
||||
|
||||
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||
@ -324,7 +402,13 @@ namespace trezor {
|
||||
// TODO: remove passphrase from memory
|
||||
m.set_passphrase(passphrase.data(), passphrase.size());
|
||||
}
|
||||
|
||||
if (!m_device_state.empty()){
|
||||
m.set_allocated_state(&m_device_state);
|
||||
}
|
||||
|
||||
resp = call_raw(&m);
|
||||
m.release_state();
|
||||
}
|
||||
|
||||
void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
|
||||
@ -332,10 +416,7 @@ namespace trezor {
|
||||
MDEBUG("on_passhprase_state_request");
|
||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||
|
||||
if (m_callback){
|
||||
m_callback->on_passphrase_state_request(msg->state());
|
||||
}
|
||||
|
||||
m_device_state = msg->state();
|
||||
messages::common::PassphraseStateAck m;
|
||||
resp = call_raw(&m);
|
||||
}
|
||||
|
@ -57,17 +57,6 @@ namespace trezor {
|
||||
#ifdef WITH_DEVICE_TREZOR
|
||||
class device_trezor_base;
|
||||
|
||||
/**
|
||||
* Trezor device callbacks
|
||||
*/
|
||||
class trezor_callback {
|
||||
public:
|
||||
virtual void on_button_request() {};
|
||||
virtual void on_pin_request(epee::wipeable_string & pin) {};
|
||||
virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {};
|
||||
virtual void on_passphrase_state_request(const std::string & state) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* TREZOR device template with basic functions
|
||||
*/
|
||||
@ -79,9 +68,12 @@ namespace trezor {
|
||||
mutable boost::mutex command_locker;
|
||||
|
||||
std::shared_ptr<Transport> m_transport;
|
||||
std::shared_ptr<trezor_callback> m_callback;
|
||||
i_device_callback * m_callback;
|
||||
|
||||
std::string full_name;
|
||||
std::string m_full_name;
|
||||
std::vector<unsigned int> m_wallet_deriv_path;
|
||||
std::string m_device_state; // returned after passphrase entry, session
|
||||
std::shared_ptr<messages::management::Features> m_features; // features from the last device reset
|
||||
|
||||
cryptonote::network_type network_type;
|
||||
|
||||
@ -90,8 +82,11 @@ namespace trezor {
|
||||
//
|
||||
|
||||
void require_connected();
|
||||
void require_initialized();
|
||||
void call_ping_unsafe();
|
||||
void test_ping();
|
||||
void device_state_reset_unsafe();
|
||||
void ensure_derivation_path() noexcept;
|
||||
|
||||
// Communication methods
|
||||
|
||||
@ -139,7 +134,7 @@ namespace trezor {
|
||||
// Scoped session closer
|
||||
BOOST_SCOPE_EXIT_ALL(&, this) {
|
||||
if (open_session){
|
||||
this->getTransport()->close();
|
||||
this->get_transport()->close();
|
||||
}
|
||||
};
|
||||
|
||||
@ -187,9 +182,13 @@ namespace trezor {
|
||||
msg->add_address_n(x);
|
||||
}
|
||||
} else {
|
||||
ensure_derivation_path();
|
||||
for (unsigned int i : DEFAULT_BIP44_PATH) {
|
||||
msg->add_address_n(i);
|
||||
}
|
||||
for (unsigned int i : m_wallet_deriv_path) {
|
||||
msg->add_address_n(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (network_type){
|
||||
@ -212,16 +211,26 @@ namespace trezor {
|
||||
bool reset();
|
||||
|
||||
// Default derivation path for Monero
|
||||
static const uint32_t DEFAULT_BIP44_PATH[3];
|
||||
static const uint32_t DEFAULT_BIP44_PATH[2];
|
||||
|
||||
std::shared_ptr<Transport> getTransport(){
|
||||
std::shared_ptr<Transport> get_transport(){
|
||||
return m_transport;
|
||||
}
|
||||
|
||||
std::shared_ptr<trezor_callback> getCallback(){
|
||||
void set_callback(i_device_callback * callback) override {
|
||||
m_callback = callback;
|
||||
}
|
||||
|
||||
i_device_callback * get_callback(){
|
||||
return m_callback;
|
||||
}
|
||||
|
||||
std::shared_ptr<messages::management::Features> & get_features() {
|
||||
return m_features;
|
||||
}
|
||||
|
||||
void set_derivation_path(const std::string &deriv_path) override;
|
||||
|
||||
/* ======================================================================= */
|
||||
/* SETUP/TEARDOWN */
|
||||
/* ======================================================================= */
|
||||
@ -249,6 +258,11 @@ namespace trezor {
|
||||
*/
|
||||
bool ping();
|
||||
|
||||
/**
|
||||
* Performs Initialize call to the Trezor, resets to known state.
|
||||
*/
|
||||
void device_state_reset();
|
||||
|
||||
// Protocol callbacks
|
||||
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
|
||||
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
|
||||
|
@ -10,15 +10,29 @@ Install `protoc` for your distribution. Requirements:
|
||||
|
||||
|
||||
Soft requirement: Python 3, can be easily installed with [pyenv].
|
||||
If Python 3 is used there are no additional python dependencies.
|
||||
|
||||
### Python 2
|
||||
Since Cmake 3.12 the `FindPython` module is used to locate the Python
|
||||
interpreter in your system. It preferably searches for Python 3, if none
|
||||
is found, it searches for Python 2.
|
||||
|
||||
Workaround if there is no Python3 available:
|
||||
Lower version of the cmake uses another module which does not guarantee
|
||||
ordering. If you want to override the selected python you can do it in
|
||||
the following way:
|
||||
|
||||
```bash
|
||||
pip install backports.tempfile
|
||||
export TREZOR_PYTHON=`which python3`
|
||||
```
|
||||
|
||||
|
||||
### Python 2.7+
|
||||
|
||||
Python 3 has `tempfile.TemporaryDirectory` available but Python 2 lacks
|
||||
this class so the message generation code uses `backports.tempfile` package
|
||||
bundled in the repository.
|
||||
|
||||
The minimal Python versions are 2.7 and 3.4
|
||||
|
||||
### Regenerate messages
|
||||
|
||||
```bash
|
||||
|
@ -14,12 +14,18 @@ import hashlib
|
||||
try:
|
||||
from tempfile import TemporaryDirectory
|
||||
except:
|
||||
# Py2 backward compatibility, optionally installed by user
|
||||
# pip install backports.tempfile
|
||||
# Py2 backward compatibility, using bundled sources.
|
||||
# Original source: pip install backports.tempfile
|
||||
try:
|
||||
from backports.tempfile import TemporaryDirectory
|
||||
# Try bundled python version
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
from py2backports.tempfile import TemporaryDirectory
|
||||
|
||||
except:
|
||||
raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile')
|
||||
raise EnvironmentError('Python 2.7+ or 3.4+ is required. '
|
||||
'TemporaryDirectory is not available in Python 2.'
|
||||
'Try to specify python to use, e.g.: "export TREZOR_PYTHON=`which python3`"')
|
||||
|
||||
|
||||
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
|
||||
|
72
src/device_trezor/trezor/tools/py2backports/tempfile.py
Normal file
72
src/device_trezor/trezor/tools/py2backports/tempfile.py
Normal file
@ -0,0 +1,72 @@
|
||||
"""
|
||||
https://github.com/pjdelport/backports.tempfile/blob/master/src/backports/tempfile.py
|
||||
Partial backport of Python 3.5's tempfile module:
|
||||
TemporaryDirectory
|
||||
Backport modifications are marked with marked with "XXX backport".
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import warnings as _warnings
|
||||
from shutil import rmtree as _rmtree
|
||||
|
||||
from py2backports.weakref import finalize
|
||||
|
||||
|
||||
# XXX backport: Rather than backporting all of mkdtemp(), we just create a
|
||||
# thin wrapper implementing its Python 3.5 signature.
|
||||
if sys.version_info < (3, 5):
|
||||
from tempfile import mkdtemp as old_mkdtemp
|
||||
|
||||
def mkdtemp(suffix=None, prefix=None, dir=None):
|
||||
"""
|
||||
Wrap `tempfile.mkdtemp()` to make the suffix and prefix optional (like Python 3.5).
|
||||
"""
|
||||
kwargs = {k: v for (k, v) in
|
||||
dict(suffix=suffix, prefix=prefix, dir=dir).items()
|
||||
if v is not None}
|
||||
return old_mkdtemp(**kwargs)
|
||||
|
||||
else:
|
||||
from tempfile import mkdtemp
|
||||
|
||||
|
||||
# XXX backport: ResourceWarning was added in Python 3.2.
|
||||
# For earlier versions, fall back to RuntimeWarning instead.
|
||||
_ResourceWarning = RuntimeWarning if sys.version_info < (3, 2) else ResourceWarning
|
||||
|
||||
|
||||
class TemporaryDirectory(object):
|
||||
"""Create and return a temporary directory. This has the same
|
||||
behavior as mkdtemp but can be used as a context manager. For
|
||||
example:
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
...
|
||||
Upon exiting the context, the directory and everything contained
|
||||
in it are removed.
|
||||
"""
|
||||
|
||||
def __init__(self, suffix=None, prefix=None, dir=None):
|
||||
self.name = mkdtemp(suffix, prefix, dir)
|
||||
self._finalizer = finalize(
|
||||
self, self._cleanup, self.name,
|
||||
warn_message="Implicitly cleaning up {!r}".format(self))
|
||||
|
||||
@classmethod
|
||||
def _cleanup(cls, name, warn_message):
|
||||
_rmtree(name)
|
||||
_warnings.warn(warn_message, _ResourceWarning)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "<{} {!r}>".format(self.__class__.__name__, self.name)
|
||||
|
||||
def __enter__(self):
|
||||
return self.name
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self):
|
||||
if self._finalizer.detach():
|
||||
_rmtree(self.name)
|
148
src/device_trezor/trezor/tools/py2backports/weakref.py
Normal file
148
src/device_trezor/trezor/tools/py2backports/weakref.py
Normal file
@ -0,0 +1,148 @@
|
||||
"""
|
||||
https://github.com/pjdelport/backports.weakref/blob/master/src/backports/weakref.py
|
||||
Partial backport of Python 3.6's weakref module:
|
||||
finalize (new in Python 3.4)
|
||||
Backport modifications are marked with "XXX backport".
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import itertools
|
||||
import sys
|
||||
from weakref import ref
|
||||
|
||||
__all__ = ['finalize']
|
||||
|
||||
|
||||
class finalize(object):
|
||||
"""Class for finalization of weakrefable objects
|
||||
finalize(obj, func, *args, **kwargs) returns a callable finalizer
|
||||
object which will be called when obj is garbage collected. The
|
||||
first time the finalizer is called it evaluates func(*arg, **kwargs)
|
||||
and returns the result. After this the finalizer is dead, and
|
||||
calling it just returns None.
|
||||
When the program exits any remaining finalizers for which the
|
||||
atexit attribute is true will be run in reverse order of creation.
|
||||
By default atexit is true.
|
||||
"""
|
||||
|
||||
# Finalizer objects don't have any state of their own. They are
|
||||
# just used as keys to lookup _Info objects in the registry. This
|
||||
# ensures that they cannot be part of a ref-cycle.
|
||||
|
||||
__slots__ = ()
|
||||
_registry = {}
|
||||
_shutdown = False
|
||||
_index_iter = itertools.count()
|
||||
_dirty = False
|
||||
_registered_with_atexit = False
|
||||
|
||||
class _Info(object):
|
||||
__slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
|
||||
|
||||
def __init__(self, obj, func, *args, **kwargs):
|
||||
if not self._registered_with_atexit:
|
||||
# We may register the exit function more than once because
|
||||
# of a thread race, but that is harmless
|
||||
import atexit
|
||||
atexit.register(self._exitfunc)
|
||||
finalize._registered_with_atexit = True
|
||||
info = self._Info()
|
||||
info.weakref = ref(obj, self)
|
||||
info.func = func
|
||||
info.args = args
|
||||
info.kwargs = kwargs or None
|
||||
info.atexit = True
|
||||
info.index = next(self._index_iter)
|
||||
self._registry[self] = info
|
||||
finalize._dirty = True
|
||||
|
||||
def __call__(self, _=None):
|
||||
"""If alive then mark as dead and return func(*args, **kwargs);
|
||||
otherwise return None"""
|
||||
info = self._registry.pop(self, None)
|
||||
if info and not self._shutdown:
|
||||
return info.func(*info.args, **(info.kwargs or {}))
|
||||
|
||||
def detach(self):
|
||||
"""If alive then mark as dead and return (obj, func, args, kwargs);
|
||||
otherwise return None"""
|
||||
info = self._registry.get(self)
|
||||
obj = info and info.weakref()
|
||||
if obj is not None and self._registry.pop(self, None):
|
||||
return (obj, info.func, info.args, info.kwargs or {})
|
||||
|
||||
def peek(self):
|
||||
"""If alive then return (obj, func, args, kwargs);
|
||||
otherwise return None"""
|
||||
info = self._registry.get(self)
|
||||
obj = info and info.weakref()
|
||||
if obj is not None:
|
||||
return (obj, info.func, info.args, info.kwargs or {})
|
||||
|
||||
@property
|
||||
def alive(self):
|
||||
"""Whether finalizer is alive"""
|
||||
return self in self._registry
|
||||
|
||||
@property
|
||||
def atexit(self):
|
||||
"""Whether finalizer should be called at exit"""
|
||||
info = self._registry.get(self)
|
||||
return bool(info) and info.atexit
|
||||
|
||||
@atexit.setter
|
||||
def atexit(self, value):
|
||||
info = self._registry.get(self)
|
||||
if info:
|
||||
info.atexit = bool(value)
|
||||
|
||||
def __repr__(self):
|
||||
info = self._registry.get(self)
|
||||
obj = info and info.weakref()
|
||||
if obj is None:
|
||||
return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
|
||||
else:
|
||||
return '<%s object at %#x; for %r at %#x>' % \
|
||||
(type(self).__name__, id(self), type(obj).__name__, id(obj))
|
||||
|
||||
@classmethod
|
||||
def _select_for_exit(cls):
|
||||
# Return live finalizers marked for exit, oldest first
|
||||
L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
|
||||
L.sort(key=lambda item:item[1].index)
|
||||
return [f for (f,i) in L]
|
||||
|
||||
@classmethod
|
||||
def _exitfunc(cls):
|
||||
# At shutdown invoke finalizers for which atexit is true.
|
||||
# This is called once all other non-daemonic threads have been
|
||||
# joined.
|
||||
reenable_gc = False
|
||||
try:
|
||||
if cls._registry:
|
||||
import gc
|
||||
if gc.isenabled():
|
||||
reenable_gc = True
|
||||
gc.disable()
|
||||
pending = None
|
||||
while True:
|
||||
if pending is None or finalize._dirty:
|
||||
pending = cls._select_for_exit()
|
||||
finalize._dirty = False
|
||||
if not pending:
|
||||
break
|
||||
f = pending.pop()
|
||||
try:
|
||||
# gc is disabled, so (assuming no daemonic
|
||||
# threads) the following is the only line in
|
||||
# this function which might trigger creation
|
||||
# of a new finalizer
|
||||
f()
|
||||
except Exception:
|
||||
sys.excepthook(*sys.exc_info())
|
||||
assert f not in cls._registry
|
||||
finally:
|
||||
# prevent any more finalizers from executing during shutdown
|
||||
finalize._shutdown = True
|
||||
if reenable_gc:
|
||||
gc.enable()
|
@ -840,7 +840,7 @@ namespace trezor{
|
||||
throw exc::DeviceAcquireException("Unable to claim libusb device");
|
||||
}
|
||||
|
||||
m_conn_count += 1;
|
||||
m_conn_count = 1;
|
||||
m_proto->session_begin(*this);
|
||||
|
||||
#undef TREZOR_DESTROY_SESSION
|
||||
|
@ -46,13 +46,34 @@ using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
rct::Bulletproof make_dummy_bulletproof(size_t n_outs)
|
||||
rct::Bulletproof make_dummy_bulletproof(const std::vector<uint64_t> &outamounts, rct::keyV &C, rct::keyV &masks)
|
||||
{
|
||||
const size_t n_outs = outamounts.size();
|
||||
const rct::key I = rct::identity();
|
||||
size_t nrl = 0;
|
||||
while ((1u << nrl) < n_outs)
|
||||
++nrl;
|
||||
nrl += 6;
|
||||
|
||||
C.resize(n_outs);
|
||||
masks.resize(n_outs);
|
||||
for (size_t i = 0; i < n_outs; ++i)
|
||||
{
|
||||
masks[i] = I;
|
||||
rct::key sv8, sv;
|
||||
sv = rct::zero();
|
||||
sv.bytes[0] = outamounts[i] & 255;
|
||||
sv.bytes[1] = (outamounts[i] >> 8) & 255;
|
||||
sv.bytes[2] = (outamounts[i] >> 16) & 255;
|
||||
sv.bytes[3] = (outamounts[i] >> 24) & 255;
|
||||
sv.bytes[4] = (outamounts[i] >> 32) & 255;
|
||||
sv.bytes[5] = (outamounts[i] >> 40) & 255;
|
||||
sv.bytes[6] = (outamounts[i] >> 48) & 255;
|
||||
sv.bytes[7] = (outamounts[i] >> 56) & 255;
|
||||
sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes);
|
||||
rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H);
|
||||
}
|
||||
|
||||
return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
|
||||
}
|
||||
}
|
||||
@ -769,9 +790,7 @@ namespace rct {
|
||||
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
|
||||
{
|
||||
// use a fake bulletproof for speed
|
||||
rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts.size()));
|
||||
C = rct::keyV(outamounts.size(), I);
|
||||
masks = rct::keyV(outamounts.size(), I);
|
||||
rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -799,9 +818,7 @@ namespace rct {
|
||||
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
|
||||
{
|
||||
// use a fake bulletproof for speed
|
||||
rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size()));
|
||||
C = rct::keyV(batch_amounts.size(), I);
|
||||
masks = rct::keyV(batch_amounts.size(), I);
|
||||
rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -187,7 +187,8 @@ namespace rct {
|
||||
rct::keyV L, R;
|
||||
rct::key a, b, t;
|
||||
|
||||
Bulletproof() {}
|
||||
Bulletproof():
|
||||
A({}), S({}), T1({}), T2({}), taux({}), mu({}), a({}), b({}), t({}) {}
|
||||
Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
|
||||
V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
|
||||
Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
|
||||
|
@ -2032,6 +2032,18 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res)
|
||||
{
|
||||
PERF_TIMER(on_pop_blocks);
|
||||
|
||||
m_core.get_blockchain_storage().pop_blocks(req.nblocks);
|
||||
|
||||
res.height = m_core.get_current_blockchain_height();
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp)
|
||||
{
|
||||
PERF_TIMER(on_relay_tx);
|
||||
|
@ -117,6 +117,7 @@ namespace cryptonote
|
||||
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
|
||||
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
|
||||
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
|
||||
MAP_URI_AUTO_JON2_IF("/pop_blocks", on_pop_blocks, COMMAND_RPC_POP_BLOCKS, !m_restricted)
|
||||
BEGIN_JSON_RPC_MAP("/json_rpc")
|
||||
MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
|
||||
MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
|
||||
@ -188,6 +189,7 @@ namespace cryptonote
|
||||
bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res);
|
||||
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res);
|
||||
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res);
|
||||
bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res);
|
||||
|
||||
//json_rpc
|
||||
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res);
|
||||
|
@ -2328,4 +2328,27 @@ namespace cryptonote
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_POP_BLOCKS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
uint64_t nblocks;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(nblocks);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string status;
|
||||
uint64_t height;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(height)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -43,8 +43,25 @@ namespace rpc
|
||||
|
||||
std::vector<std::uint64_t> distribution;
|
||||
std::uint64_t start_height, base;
|
||||
if (!f(amount, from_height, to_height, start_height, distribution, base))
|
||||
return boost::none;
|
||||
|
||||
// see if we can extend the cache - a common case
|
||||
if (d.cached && amount == 0 && d.cached_from == from_height && to_height > d.cached_to)
|
||||
{
|
||||
std::vector<std::uint64_t> new_distribution;
|
||||
if (!f(amount, d.cached_to + 1, to_height, start_height, new_distribution, base))
|
||||
return boost::none;
|
||||
distribution = d.cached_distribution;
|
||||
distribution.reserve(distribution.size() + new_distribution.size());
|
||||
for (const auto &e: new_distribution)
|
||||
distribution.push_back(e);
|
||||
start_height = d.cached_start_height;
|
||||
base = d.cached_base;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!f(amount, from_height, to_height, start_height, distribution, base))
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (to_height > 0 && to_height >= from_height)
|
||||
{
|
||||
|
@ -3790,6 +3790,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
m_wallet->callback(this);
|
||||
if (!m_wallet)
|
||||
{
|
||||
return {};
|
||||
@ -3807,9 +3808,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
m_wallet->set_refresh_from_block_height(m_restore_height);
|
||||
|
||||
auto device_desc = tools::wallet2::device_name_option(vm);
|
||||
auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm);
|
||||
try
|
||||
{
|
||||
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
|
||||
m_wallet->device_derivation_path(device_derivation_path);
|
||||
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
|
||||
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
|
||||
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
@ -3897,7 +3900,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
||||
epee::wipeable_string password;
|
||||
try
|
||||
{
|
||||
auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter);
|
||||
auto rc = tools::wallet2::make_from_file(vm, false, "", password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
password = std::move(std::move(rc.second).password());
|
||||
if (!m_wallet)
|
||||
@ -3905,6 +3908,8 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_wallet->callback(this);
|
||||
m_wallet->load(m_wallet_file, password);
|
||||
std::string prefix;
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
@ -4308,6 +4313,61 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char
|
||||
return pwd_container->password();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_button_request()
|
||||
{
|
||||
message_writer(console_color_white, false) << tr("Device requires attention");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_pin_request(epee::wipeable_string & pin)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
std::string msg = tr("Enter device PIN");
|
||||
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN"));
|
||||
pin = pwd_container->password();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||
{
|
||||
if (on_device){
|
||||
message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
std::string msg = tr("Enter device passphrase");
|
||||
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase"));
|
||||
passphrase = pwd_container->password();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money)
|
||||
{
|
||||
// Key image sync after the first refresh
|
||||
if (!m_wallet->get_account().get_device().has_tx_cold_sign()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!received_money || m_wallet->get_device_last_key_image_sync() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finished first refresh for HW device and money received -> KI sync
|
||||
message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received money. hw_key_images_sync is needed. ");
|
||||
|
||||
std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
|
||||
if (std::cin.eof() || !command_line::is_yes(accepted)) {
|
||||
message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer.");
|
||||
return;
|
||||
}
|
||||
|
||||
key_images_sync_intern();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
|
||||
{
|
||||
if (!try_connect_to_daemon(is_init))
|
||||
@ -4325,13 +4385,14 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||
message_writer() << tr("Starting refresh...");
|
||||
|
||||
uint64_t fetched_blocks = 0;
|
||||
bool received_money = false;
|
||||
bool ok = false;
|
||||
std::ostringstream ss;
|
||||
try
|
||||
{
|
||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks);
|
||||
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money);
|
||||
ok = true;
|
||||
// Clear line "Height xxx of xxx"
|
||||
std::cout << "\r \r";
|
||||
@ -4339,6 +4400,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||
if (is_init)
|
||||
print_accounts();
|
||||
show_balance_unlocked();
|
||||
on_refresh_finished(start_height, fetched_blocks, is_init, received_money);
|
||||
}
|
||||
catch (const tools::error::daemon_busy&)
|
||||
{
|
||||
@ -5946,12 +6008,6 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::donate(const std::vector<std::string> &args_)
|
||||
{
|
||||
if(m_wallet->nettype() != cryptonote::MAINNET)
|
||||
{
|
||||
fail_msg_writer() << tr("donations are not enabled on the testnet or on the stagenet");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args = args_;
|
||||
if(local_args.empty() || local_args.size() > 5)
|
||||
{
|
||||
@ -5973,11 +6029,30 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
|
||||
amount_str = local_args.back();
|
||||
local_args.pop_back();
|
||||
// push back address, amount, payment id
|
||||
local_args.push_back(MONERO_DONATION_ADDR);
|
||||
std::string address_str;
|
||||
if (m_wallet->nettype() != cryptonote::MAINNET)
|
||||
{
|
||||
// if not mainnet, convert donation address string to the relevant network type
|
||||
address_parse_info info;
|
||||
if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, MONERO_DONATION_ADDR))
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to parse donation address: ") << MONERO_DONATION_ADDR;
|
||||
return true;
|
||||
}
|
||||
address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
|
||||
}
|
||||
else
|
||||
{
|
||||
address_str = MONERO_DONATION_ADDR;
|
||||
}
|
||||
local_args.push_back(address_str);
|
||||
local_args.push_back(amount_str);
|
||||
if (!payment_id_str.empty())
|
||||
local_args.push_back(payment_id_str);
|
||||
message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str();
|
||||
if (m_wallet->nettype() == cryptonote::MAINNET)
|
||||
message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str();
|
||||
else
|
||||
message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str();
|
||||
transfer(local_args);
|
||||
return true;
|
||||
}
|
||||
@ -8096,13 +8171,13 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
|
||||
return true;
|
||||
}
|
||||
if (!m_wallet->is_trusted_daemon())
|
||||
{
|
||||
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
key_images_sync_intern();
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::key_images_sync_intern(){
|
||||
try
|
||||
{
|
||||
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
|
||||
@ -8111,19 +8186,23 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
|
||||
if (height > 0)
|
||||
{
|
||||
success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
|
||||
<< print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
||||
} else {
|
||||
success_msg_writer() << tr("Key images synchronized to height ") << height;
|
||||
if (!m_wallet->is_trusted_daemon())
|
||||
{
|
||||
message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
|
||||
} else
|
||||
{
|
||||
success_msg_writer() << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail_msg_writer() << tr("Failed to import key images");
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
|
||||
|
@ -241,6 +241,8 @@ namespace cryptonote
|
||||
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
|
||||
std::string get_prompt() const;
|
||||
bool print_seed(bool encrypted);
|
||||
void key_images_sync_intern();
|
||||
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
|
||||
|
||||
struct transfer_view
|
||||
{
|
||||
@ -287,6 +289,9 @@ namespace cryptonote
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
|
||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
|
||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
|
||||
virtual void on_button_request();
|
||||
virtual void on_pin_request(epee::wipeable_string & pin);
|
||||
virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
|
||||
//----------------------------------------------------------
|
||||
|
||||
friend class refresh_progress_reporter_t;
|
||||
|
@ -222,6 +222,7 @@ struct options {
|
||||
};
|
||||
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
|
||||
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
|
||||
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
|
||||
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
|
||||
};
|
||||
|
||||
@ -274,6 +275,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
|
||||
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
||||
auto device_name = command_line::get_arg(vm, opts.hw_device);
|
||||
auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
|
||||
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
|
||||
@ -329,6 +331,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||
wallet->set_ring_database(ringdb_path.string());
|
||||
wallet->device_name(device_name);
|
||||
wallet->device_derivation_path(device_derivation_path);
|
||||
|
||||
try
|
||||
{
|
||||
@ -838,6 +841,24 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
|
||||
}
|
||||
}
|
||||
|
||||
void wallet_device_callback::on_button_request()
|
||||
{
|
||||
if (wallet)
|
||||
wallet->on_button_request();
|
||||
}
|
||||
|
||||
void wallet_device_callback::on_pin_request(epee::wipeable_string & pin)
|
||||
{
|
||||
if (wallet)
|
||||
wallet->on_pin_request(pin);
|
||||
}
|
||||
|
||||
void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||
{
|
||||
if (wallet)
|
||||
wallet->on_passphrase_request(on_device, passphrase);
|
||||
}
|
||||
|
||||
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||
m_multisig_rescan_info(NULL),
|
||||
m_multisig_rescan_k(NULL),
|
||||
@ -891,7 +912,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||
m_ringdb(),
|
||||
m_last_block_reward(0),
|
||||
m_encrypt_keys_after_refresh(boost::none),
|
||||
m_unattended(unattended)
|
||||
m_unattended(unattended),
|
||||
m_device_last_key_image_sync(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -914,6 +936,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_
|
||||
return command_line::get_arg(vm, options().hw_device);
|
||||
}
|
||||
|
||||
std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
|
||||
{
|
||||
return command_line::get_arg(vm, options().hw_device_derivation_path);
|
||||
}
|
||||
|
||||
void wallet2::init_options(boost::program_options::options_description& desc_params)
|
||||
{
|
||||
const options opts{};
|
||||
@ -930,6 +957,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
|
||||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
||||
command_line::add_arg(desc_params, opts.hw_device);
|
||||
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
|
||||
command_line::add_arg(desc_params, opts.tx_notify);
|
||||
}
|
||||
|
||||
@ -949,7 +977,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
||||
return {nullptr, password_container{}};
|
||||
}
|
||||
auto wallet = make_basic(vm, unattended, opts, password_prompter);
|
||||
if (wallet)
|
||||
if (wallet && !wallet_file.empty())
|
||||
{
|
||||
wallet->load(wallet_file, pwd->password());
|
||||
}
|
||||
@ -1091,15 +1119,17 @@ bool wallet2::reconnect_device()
|
||||
hw::device &hwdev = lookup_device(m_device_name);
|
||||
hwdev.set_name(m_device_name);
|
||||
hwdev.set_network_type(m_nettype);
|
||||
hwdev.set_derivation_path(m_device_derivation_path);
|
||||
hwdev.set_callback(get_device_callback());
|
||||
r = hwdev.init();
|
||||
if (!r){
|
||||
LOG_PRINT_L2("Could not init device");
|
||||
MERROR("Could not init device");
|
||||
return false;
|
||||
}
|
||||
|
||||
r = hwdev.connect();
|
||||
if (!r){
|
||||
LOG_PRINT_L2("Could not connect to the device");
|
||||
MERROR("Could not connect to the device");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2998,6 +3028,7 @@ bool wallet2::clear()
|
||||
m_subaddresses.clear();
|
||||
m_subaddress_labels.clear();
|
||||
m_multisig_rounds_passed = 0;
|
||||
m_device_last_key_image_sync = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3159,6 +3190,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||
value.SetString(m_device_name.c_str(), m_device_name.size());
|
||||
json.AddMember("device_name", value, json.GetAllocator());
|
||||
|
||||
value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
|
||||
json.AddMember("device_derivation_path", value, json.GetAllocator());
|
||||
|
||||
// Serialize the JSON object
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
@ -3278,6 +3312,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
||||
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
||||
m_device_name = "";
|
||||
m_device_derivation_path = "";
|
||||
m_key_device_type = hw::device::device_type::SOFTWARE;
|
||||
encrypted_secret_keys = false;
|
||||
}
|
||||
@ -3445,6 +3480,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
|
||||
}
|
||||
}
|
||||
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
|
||||
m_device_derivation_path = field_device_derivation_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3459,6 +3497,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
hw::device &hwdev = lookup_device(m_device_name);
|
||||
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
|
||||
hwdev.set_network_type(m_nettype);
|
||||
hwdev.set_derivation_path(m_device_derivation_path);
|
||||
hwdev.set_callback(get_device_callback());
|
||||
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
|
||||
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
|
||||
m_account.set_device(hwdev);
|
||||
@ -3965,6 +4005,8 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
|
||||
auto &hwdev = lookup_device(device_name);
|
||||
hwdev.set_name(device_name);
|
||||
hwdev.set_network_type(m_nettype);
|
||||
hwdev.set_derivation_path(m_device_derivation_path);
|
||||
hwdev.set_callback(get_device_callback());
|
||||
|
||||
m_account.create_from_device(hwdev);
|
||||
m_key_device_type = m_account.get_device().get_type();
|
||||
@ -9232,9 +9274,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||
auto & hwdev = get_account().get_device();
|
||||
if (!hwdev.has_ki_cold_sync()){
|
||||
throw std::invalid_argument("Device does not support cold ki sync protocol");
|
||||
}
|
||||
CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
|
||||
|
||||
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
||||
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
||||
@ -9245,7 +9285,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||
|
||||
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
|
||||
|
||||
return import_key_images(ski, 0, spent, unspent);
|
||||
// Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
|
||||
uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
|
||||
m_device_last_key_image_sync = time(NULL);
|
||||
|
||||
return import_res;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
|
||||
@ -11975,4 +12019,29 @@ uint64_t wallet2::get_segregation_fork_height() const
|
||||
void wallet2::generate_genesis(cryptonote::block& b) const {
|
||||
cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
wallet_device_callback * wallet2::get_device_callback()
|
||||
{
|
||||
if (!m_device_callback){
|
||||
m_device_callback.reset(new wallet_device_callback(this));
|
||||
}
|
||||
return m_device_callback.get();
|
||||
}//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::on_button_request()
|
||||
{
|
||||
if (0 != m_callback)
|
||||
m_callback->on_button_request();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::on_pin_request(epee::wipeable_string & pin)
|
||||
{
|
||||
if (0 != m_callback)
|
||||
m_callback->on_pin_request(pin);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||
{
|
||||
if (0 != m_callback)
|
||||
m_callback->on_passphrase_request(on_device, passphrase);
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +100,26 @@ namespace tools
|
||||
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||
// Device callbacks
|
||||
virtual void on_button_request() {}
|
||||
virtual void on_pin_request(epee::wipeable_string & pin) {}
|
||||
virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}
|
||||
// Common callbacks
|
||||
virtual void on_pool_tx_removed(const crypto::hash &txid) {}
|
||||
virtual ~i_wallet2_callback() {}
|
||||
};
|
||||
|
||||
class wallet_device_callback : public hw::i_device_callback
|
||||
{
|
||||
public:
|
||||
wallet_device_callback(wallet2 * wallet): wallet(wallet) {};
|
||||
void on_button_request() override;
|
||||
void on_pin_request(epee::wipeable_string & pin) override;
|
||||
void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override;
|
||||
private:
|
||||
wallet2 * wallet;
|
||||
};
|
||||
|
||||
struct tx_dust_policy
|
||||
{
|
||||
uint64_t dust_threshold;
|
||||
@ -156,6 +171,7 @@ namespace tools
|
||||
{
|
||||
friend class ::Serialization_portability_wallet_Test;
|
||||
friend class wallet_keys_unlocker;
|
||||
friend class wallet_device_callback;
|
||||
public:
|
||||
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
|
||||
|
||||
@ -177,6 +193,7 @@ namespace tools
|
||||
static bool has_testnet_option(const boost::program_options::variables_map& vm);
|
||||
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
|
||||
static std::string device_name_option(const boost::program_options::variables_map& vm);
|
||||
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm);
|
||||
static void init_options(boost::program_options::options_description& desc_params);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||
@ -795,6 +812,7 @@ namespace tools
|
||||
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
||||
|
||||
uint64_t get_last_block_reward() const { return m_last_block_reward; }
|
||||
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }
|
||||
|
||||
template <class t_archive>
|
||||
inline void serialize(t_archive &a, const unsigned int ver)
|
||||
@ -903,6 +921,9 @@ namespace tools
|
||||
if(ver < 26)
|
||||
return;
|
||||
a & m_tx_device;
|
||||
if(ver < 27)
|
||||
return;
|
||||
a & m_device_last_key_image_sync;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -964,6 +985,8 @@ namespace tools
|
||||
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
||||
const std::string & device_name() const { return m_device_name; }
|
||||
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
||||
const std::string & device_derivation_path() const { return m_device_derivation_path; }
|
||||
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }
|
||||
|
||||
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
||||
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
|
||||
@ -1287,6 +1310,11 @@ namespace tools
|
||||
void setup_new_blockchain();
|
||||
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
|
||||
|
||||
wallet_device_callback * get_device_callback();
|
||||
void on_button_request();
|
||||
void on_pin_request(epee::wipeable_string & pin);
|
||||
void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
|
||||
|
||||
cryptonote::account_base m_account;
|
||||
boost::optional<epee::net_utils::http::login> m_daemon_login;
|
||||
std::string m_daemon_address;
|
||||
@ -1365,6 +1393,8 @@ namespace tools
|
||||
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
||||
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
||||
std::string m_device_name;
|
||||
std::string m_device_derivation_path;
|
||||
uint64_t m_device_last_key_image_sync;
|
||||
|
||||
// Aux transaction data from device
|
||||
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
||||
@ -1398,9 +1428,10 @@ namespace tools
|
||||
bool m_devices_registered;
|
||||
|
||||
std::shared_ptr<tools::Notify> m_tx_notify;
|
||||
std::unique_ptr<wallet_device_callback> m_device_callback;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 26)
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 27)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||
|
@ -219,6 +219,14 @@ namespace tools
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct password_entry_failed : public wallet_runtime_error
|
||||
{
|
||||
explicit password_entry_failed(std::string&& loc, const std::string &msg = "Password entry failed")
|
||||
: wallet_runtime_error(std::move(loc), msg)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
const char* const file_error_messages[] = {
|
||||
"file already exists",
|
||||
"file not found",
|
||||
|
@ -104,5 +104,6 @@ namespace tests
|
||||
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
|
||||
bool fluffy_blocks_enabled() const { return false; }
|
||||
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; }
|
||||
bool pad_transactions() const { return false; }
|
||||
};
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ public:
|
||||
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
|
||||
bool fluffy_blocks_enabled() const { return false; }
|
||||
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; }
|
||||
bool pad_transactions() { return false; }
|
||||
void stop() {}
|
||||
};
|
||||
|
||||
|
@ -142,5 +142,6 @@ public:
|
||||
virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; }
|
||||
virtual bool update_pruning() { return true; }
|
||||
virtual bool check_pruning() { return true; }
|
||||
virtual void prune_outputs(uint64_t amount) {}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user