Merge pull request #156 from wowario/upstream

Upstream
pull/158/head
jw 5 years ago committed by GitHub
commit 111e25f10d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,7 +6,7 @@ $(package)_sha256_hash=1f912c54035533fb4268809701d65c7468d00e292efbc31e644490845
$(package)_patches=icu-001-dont-build-static-dynamic-twice.patch
define $(package)_set_vars
$(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 --std=gnu++0x -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC"
$(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 --std=gnu++0x -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static"
endef
define $(package)_config_cmds
@ -17,7 +17,7 @@ define $(package)_config_cmds
sh ../source/runConfigureICU Linux &&\
make &&\
cd ../buildb &&\
sh ../source/$($(package)_autoconf) --enable-static=yes --enable-shared=yes --disable-layoutex --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\
sh ../source/$($(package)_autoconf) --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\
$(MAKE) $($(package)_build_opts)
endef

@ -1,30 +0,0 @@
package=libevent
$(package)_version=2.1.8-stable
$(package)_download_path=https://github.com/libevent/libevent/archive/
$(package)_file_name=release-$($(package)_version).tar.gz
$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d
define $(package)_preprocess_cmds
./autogen.sh
endef
define $(package)_set_vars
$(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples
$(package)_config_opts_release=--disable-debug-mode
$(package)_config_opts_linux=--with-pic
endef
define $(package)_config_cmds
$($(package)_autoconf)
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
endef

@ -1,4 +1,4 @@
packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi protobuf libusb
packages:=boost openssl zeromq cppzmq expat ldns cppzmq readline libiconv qt hidapi protobuf libusb
native_packages := native_ccache native_protobuf
darwin_native_packages = native_biplist native_ds_store native_mac_alias
@ -17,6 +17,5 @@ endif
ifneq ($(build_os),darwin)
darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus
packages += readline
endif

@ -4,7 +4,6 @@ $(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
$(package)_dependencies=openssl zlib
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib
$(package)_patches=pidlist_absolute.patch fix_qt_pkgconfig.patch qfixed-coretext.patch
@ -62,14 +61,14 @@ $(package)_config_opts += -no-xrender
$(package)_config_opts += -nomake examples
$(package)_config_opts += -nomake tests
$(package)_config_opts += -opensource
$(package)_config_opts += -openssl-linked
$(package)_config_opts += -no-openssl
$(package)_config_opts += -optimized-qmake
$(package)_config_opts += -pch
$(package)_config_opts += -pkg-config
$(package)_config_opts += -qt-libpng
$(package)_config_opts += -qt-libjpeg
$(package)_config_opts += -no-libpng
$(package)_config_opts += -no-libjpeg
$(package)_config_opts += -qt-pcre
$(package)_config_opts += -system-zlib
$(package)_config_opts += -no-zlib
$(package)_config_opts += -reduce-exports
$(package)_config_opts += -static
$(package)_config_opts += -silent
@ -124,7 +123,6 @@ define $(package)_config_cmds
export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \
export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \
./configure $($(package)_config_opts) && \
echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \
echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \
$(MAKE) sub-src-clean && \
cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \

@ -1,27 +0,0 @@
package=zlib
$(package)_version=1.2.11
$(package)_download_path=https://www.zlib.net
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
define $(package)_set_vars
$(package)_build_opts= CC="$($(package)_cc)"
$(package)_build_opts+=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC"
$(package)_build_opts+=RANLIB="$($(package)_ranlib)"
$(package)_build_opts+=AR="$($(package)_ar)"
$(package)_build_opts_darwin+=AR="$($(package)_libtool)"
$(package)_build_opts_darwin+=ARFLAGS="-o"
endef
define $(package)_config_cmds
./configure --static --prefix=$(host_prefix)
endef
define $(package)_build_cmds
$(MAKE) $($(package)_build_opts) libz.a
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts)
endef

@ -29,6 +29,7 @@
#pragma once
#include <algorithm>
#include <boost/utility/string_ref.hpp>
namespace epee
{
@ -36,6 +37,40 @@ namespace misc_utils
{
namespace parse
{
// 1: digit
// 2: .eE (floating point)
// 4: alpha
// 8: whitespace
// 16: allowed in float but doesn't necessarily mean it's a float
static const constexpr uint8_t lut[256]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, // 16
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 18, 0, // 48
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, // 64
0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 80
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 96
0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 112
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 128
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
inline bool isspace(char c)
{
return lut[(uint8_t)c] & 8;
}
inline bool isdigit(char c)
{
return lut[(uint8_t)c] & 1;
}
inline std::string transform_to_escape_sequence(const std::string& src)
{
static const char escaped[] = "\b\f\n\r\t\v\"\\/";
@ -159,25 +194,34 @@ namespace misc_utils
return false;
}
}
inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val, bool& is_float_val, bool& is_signed_val)
inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val, bool& is_float_val, bool& is_signed_val)
{
val.clear();
is_float_val = false;
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
uint8_t float_flag = 0;
is_signed_val = false;
size_t chars = 0;
std::string::const_iterator it = star_end_string;
if (it != buf_end && *it == '-')
{
is_signed_val = true;
++chars;
++it;
}
for(;it != buf_end;it++)
{
if(isdigit(*it) || (it == star_end_string && *it == '-') || (val.size() && *it == '.' ) || (is_float_val && (*it == 'e' || *it == 'E' || *it == '-' || *it == '+' )) )
const uint8_t flags = lut[(uint8_t)*it];
if (flags & 16)
{
if(!val.size() && *it == '-')
is_signed_val = true;
if(*it == '.' )
is_float_val = true;
val.push_back(*it);
float_flag |= flags;
++chars;
}
else
{
val = boost::string_ref(&*star_end_string, chars);
if(val.size())
{
star_end_string = --it;
is_float_val = !!(float_flag & 2);
return;
}
else
@ -186,7 +230,7 @@ namespace misc_utils
}
ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end));
}
inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val)
{
try
{
@ -199,15 +243,15 @@ namespace misc_utils
return false;
}
}
inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val)
{
val.clear();
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
{
if(!isalpha(*it))
if (!(lut[(uint8_t)*it] & 4))
{
val.assign(star_end_string, it);
val = boost::string_ref(&*star_end_string, std::distance(star_end_string, it));
if(val.size())
{
star_end_string = --it;
@ -218,7 +262,7 @@ namespace misc_utils
}
ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end));
}
inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val)
{
try
{

@ -39,7 +39,7 @@ namespace epee
{
namespace json
{
#define CHECK_ISSPACE() if(!isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));}
#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));}
/*inline void parse_error()
{
@ -114,11 +114,11 @@ namespace epee
std::string val;
match_string2(it, buf_end, val);
//insert text value
stg.set_value(name, val, current_section);
stg.set_value(name, std::move(val), current_section);
state = match_state_wonder_after_value;
}else if (isdigit(*it) || *it == '-')
}else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//just a named number value started
std::string val;
boost::string_ref val;
bool is_v_float = false;bool is_signed = false;
match_number2(it, buf_end, val, is_v_float, is_signed);
if(!is_v_float)
@ -126,27 +126,27 @@ namespace epee
if(is_signed)
{
errno = 0;
int64_t nval = strtoll(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, nval, current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, nval, current_section);
}
}else
{
errno = 0;
double nval = strtod(val.c_str(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + val);
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, nval, current_section);
}
state = match_state_wonder_after_value;
}else if(isalpha(*it) )
{// could be null, true or false
std::string word;
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "null"))
{
@ -203,13 +203,13 @@ namespace epee
//mean array of strings
std::string val;
match_string2(it, buf_end, val);
h_array = stg.insert_first_value(name, val, current_section);
h_array = stg.insert_first_value(name, std::move(val), current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry");
state = match_state_array_after_value;
array_md = array_mode_string;
}else if (isdigit(*it) || *it == '-')
}else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//array of numbers value started
std::string val;
boost::string_ref val;
bool is_v_float = false;bool is_signed_val = false;
match_number2(it, buf_end, val, is_v_float, is_signed_val);
if(!is_v_float)
@ -217,22 +217,22 @@ namespace epee
if (is_signed_val)
{
errno = 0;
int64_t nval = strtoll(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, nval, current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, nval, current_section);
}
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}else
{
errno = 0;
double nval = strtod(val.c_str(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + val);
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, nval, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}
@ -245,7 +245,7 @@ namespace epee
state = match_state_wonder_after_value;
}else if(isalpha(*it) )
{// array of booleans
std::string word;
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{
@ -291,15 +291,15 @@ namespace epee
{
std::string val;
match_string2(it, buf_end, val);
bool res = stg.insert_next_value(h_array, val);
bool res = stg.insert_next_value(h_array, std::move(val));
CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values");
state = match_state_array_after_value;
}else CHECK_ISSPACE();
break;
case array_mode_numbers:
if (isdigit(*it) || *it == '-')
if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//array of numbers value started
std::string val;
boost::string_ref val;
bool is_v_float = false;bool is_signed_val = false;
match_number2(it, buf_end, val, is_v_float, is_signed_val);
bool insert_res = false;
@ -308,21 +308,21 @@ namespace epee
if (is_signed_val)
{
errno = 0;
int64_t nval = strtoll(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, nval);
}else
{
errno = 0;
uint64_t nval = strtoull(val.c_str(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + val);
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, nval);
}
}else
{
errno = 0;
double nval = strtod(val.c_str(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + val);
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, nval);
}
CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value");
@ -333,7 +333,7 @@ namespace epee
case array_mode_booleans:
if(isalpha(*it) )
{// array of booleans
std::string word;
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{

@ -1258,7 +1258,7 @@ public:
*
* @return the requested output data
*/
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const = 0;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt = true) const = 0;
/**
* @brief gets an output's tx hash and index

@ -2536,7 +2536,7 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const
return num_elems;
}
output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) const
output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@ -2563,7 +2563,8 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6
{
const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));;
ret.commitment = rct::zeroCommit(amount);
if (include_commitmemt)
ret.commitment = rct::zeroCommit(amount);
}
TXN_POSTFIX_RDONLY();
return ret;

@ -242,7 +242,7 @@ public:
virtual uint64_t get_num_outputs(const uint64_t& amount) const;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const;
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const;
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;

@ -51,6 +51,8 @@ using namespace epee;
using namespace cryptonote;
static bool stop_requested = false;
static uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0;
static bool opt_cache_outputs = false, opt_cache_txes = false, opt_cache_blocks = false;
struct ancestor
{
@ -137,6 +139,8 @@ struct ancestry_state_t
std::unordered_map<crypto::hash, ::tx_data_t> tx_cache;
std::vector<cryptonote::block> block_cache;
ancestry_state_t(): height(0) {}
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
{
a & height;
@ -219,6 +223,113 @@ static std::unordered_set<ancestor> get_ancestry(const std::unordered_map<crypto
return i->second;
}
static bool get_block_from_height(ancestry_state_t &state, BlockchainDB *db, uint64_t height, cryptonote::block &b)
{
++total_blocks;
if (state.block_cache.size() > height && !state.block_cache[height].miner_tx.vin.empty())
{
++cached_blocks;
b = state.block_cache[height];
return true;
}
cryptonote::blobdata bd = db->get_block_blob_from_height(height);
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return false;
}
if (opt_cache_blocks)
{
state.block_cache.resize(height + 1);
state.block_cache[height] = b;
}
return true;
}
static bool get_transaction(ancestry_state_t &state, BlockchainDB *db, const crypto::hash &txid, ::tx_data_t &tx_data)
{
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(txid);
++total_txes;
if (i != state.tx_cache.end())
{
++cached_txes;
tx_data = i->second;
return true;
}
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
return false;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << txid);
return false;
}
tx_data = ::tx_data_t(tx);
if (opt_cache_txes)
state.tx_cache.insert(std::make_pair(txid, tx_data));
return true;
}
static bool get_output_txid(ancestry_state_t &state, BlockchainDB *db, uint64_t amount, uint64_t offset, crypto::hash &txid)
{
++total_outputs;
std::unordered_map<ancestor, crypto::hash>::const_iterator i = state.output_cache.find({amount, offset});
if (i != state.output_cache.end())
{
++cached_outputs;
txid = i->second;
return true;
}
const output_data_t od = db->get_output_key(amount, offset, false);
cryptonote::block b;
if (!get_block_from_height(state, db, od.height, b))
return false;
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
txid = cryptonote::get_transaction_hash(b.miner_tx);
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, txid));
return true;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return false;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
::tx_data_t tx_data3;
if (!get_transaction(state, db, block_txid, tx_data3))
return false;
for (size_t out = 0; out < tx_data3.vout.size(); ++out)
{
if (tx_data3.vout[out] == od.pubkey)
{
txid = block_txid;
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, txid));
return true;
}
}
}
return false;
}
int main(int argc, char* argv[])
{
TRY_ENTRY();
@ -243,12 +354,13 @@ int main(int argc, char* argv[])
"database", available_dbs.c_str(), default_db_type
};
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get ancestry for this txid", ""};
const command_line::arg_descriptor<std::string> arg_output = {"output", "Get ancestry for this output (amount/offset format)", ""};
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get ancestry for all txes at this height", 0};
const command_line::arg_descriptor<bool> arg_all = {"all", "Include the whole chain", false};
const command_line::arg_descriptor<bool> arg_refresh = {"refresh", "Refresh the whole chain first", false};
const command_line::arg_descriptor<bool> arg_cache_outputs = {"cache-outputs", "Cache outputs (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_cache_txes = {"cache-txes", "Cache txes (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_cache_blocks = {"cache-blocks", "Cache blocks (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Including coinbase tx", false};
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Including coinbase tx in per height average", false};
const command_line::arg_descriptor<bool> arg_show_cache_stats = {"show-cache-stats", "Show cache statistics", false};
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
@ -257,8 +369,9 @@ int main(int argc, char* argv[])
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_txid);
command_line::add_arg(desc_cmd_sett, arg_output);
command_line::add_arg(desc_cmd_sett, arg_height);
command_line::add_arg(desc_cmd_sett, arg_all);
command_line::add_arg(desc_cmd_sett, arg_refresh);
command_line::add_arg(desc_cmd_sett, arg_cache_outputs);
command_line::add_arg(desc_cmd_sett, arg_cache_txes);
command_line::add_arg(desc_cmd_sett, arg_cache_blocks);
@ -300,20 +413,22 @@ int main(int argc, char* argv[])
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
std::string opt_output_string = command_line::get_arg(vm, arg_output);
uint64_t opt_height = command_line::get_arg(vm, arg_height);
bool opt_all = command_line::get_arg(vm, arg_all);
bool opt_cache_outputs = command_line::get_arg(vm, arg_cache_outputs);
bool opt_cache_txes = command_line::get_arg(vm, arg_cache_txes);
bool opt_cache_blocks = command_line::get_arg(vm, arg_cache_blocks);
bool opt_refresh = command_line::get_arg(vm, arg_refresh);
opt_cache_outputs = command_line::get_arg(vm, arg_cache_outputs);
opt_cache_txes = command_line::get_arg(vm, arg_cache_txes);
opt_cache_blocks = command_line::get_arg(vm, arg_cache_blocks);
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
bool opt_show_cache_stats = command_line::get_arg(vm, arg_show_cache_stats);
if ((!opt_txid_string.empty()) + !!opt_height + !!opt_all > 1)
if ((!opt_txid_string.empty()) + !!opt_height + !opt_output_string.empty() > 1)
{
std::cerr << "Only one of --txid, --height and --all can be given" << std::endl;
std::cerr << "Only one of --txid, --height, --output can be given" << std::endl;
return 1;
}
crypto::hash opt_txid = crypto::null_hash;
uint64_t output_amount = 0, output_offset = 0;
if (!opt_txid_string.empty())
{
if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid))
@ -322,6 +437,14 @@ int main(int argc, char* argv[])
return 1;
}
}
else if (!opt_output_string.empty())
{
if (sscanf(opt_output_string.c_str(), "%" SCNu64 "/%" SCNu64, &output_amount, &output_offset) != 2)
{
std::cerr << "Invalid output" << std::endl;
return 1;
}
}
std::string db_type = command_line::get_arg(vm, arg_database);
if (!cryptonote::blockchain_valid_db_type(db_type))
@ -372,37 +495,36 @@ int main(int argc, char* argv[])
std::vector<crypto::hash> start_txids;
// forward method
if (opt_all)
{
uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0;
ancestry_state_t state;
ancestry_state_t state;
const std::string state_file_path = (boost::filesystem::path(opt_data_dir) / "ancestry-state.bin").string();
LOG_PRINT_L0("Loading state data from " << state_file_path);
std::ifstream state_data_in;
state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in);
if (!state_data_in.fail())
const std::string state_file_path = (boost::filesystem::path(opt_data_dir) / "ancestry-state.bin").string();
LOG_PRINT_L0("Loading state data from " << state_file_path);
std::ifstream state_data_in;
state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in);
if (!state_data_in.fail())
{
try
{
try
{
boost::archive::portable_binary_iarchive a(state_data_in);
a >> state;
}
catch (const std::exception &e)
{
MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch");
state = ancestry_state_t();
}
state_data_in.close();
boost::archive::portable_binary_iarchive a(state_data_in);
a >> state;
}
catch (const std::exception &e)
{
MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch");
state = ancestry_state_t();
}
state_data_in.close();
}
tools::signal_handler::install([](int type) {
stop_requested = true;
});
tools::signal_handler::install([](int type) {
stop_requested = true;
});
// forward method
const uint64_t db_height = db->height();
if (opt_refresh)
{
MINFO("Starting from height " << state.height);
const uint64_t db_height = db->height();
state.block_cache.reserve(db_height);
for (uint64_t h = state.height; h < db_height; ++h)
{
@ -464,113 +586,20 @@ int main(int argc, char* argv[])
{
for (size_t ring = 0; ring < tx_data.vin.size(); ++ring)
{
if (1)
const uint64_t amount = tx_data.vin[ring].first;
const std::vector<uint64_t> &absolute_offsets = tx_data.vin[ring].second;
for (uint64_t offset: absolute_offsets)
{
const uint64_t amount = tx_data.vin[ring].first;
const std::vector<uint64_t> &absolute_offsets = tx_data.vin[ring].second;
for (uint64_t offset: absolute_offsets)
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
// find the tx which created this output
bool found = false;
crypto::hash output_txid;
if (!get_output_txid(state, db, amount, offset, output_txid))
{
const output_data_t od = db->get_output_key(amount, offset);
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
cryptonote::block b;
++total_blocks;
if (state.block_cache.size() > od.height && !state.block_cache[od.height].miner_tx.vin.empty())
{
++cached_blocks;
b = state.block_cache[od.height];
}
else
{
cryptonote::blobdata bd = db->get_block_blob_from_height(od.height);
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
if (opt_cache_blocks)
{
state.block_cache.resize(od.height + 1);
state.block_cache[od.height] = b;
}
}
// find the tx which created this output
bool found = false;
std::unordered_map<ancestor, crypto::hash>::const_iterator i = state.output_cache.find({amount, offset});
++total_outputs;
if (i != state.output_cache.end())
{
++cached_outputs;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, i->second));
found = true;
}
else for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, cryptonote::get_transaction_hash(b.miner_tx)));
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, cryptonote::get_transaction_hash(b.miner_tx)));
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return 1;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
if (found)
break;
::tx_data_t tx_data2;
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(block_txid);
++total_txes;
if (i != state.tx_cache.end())
{
++cached_txes;
tx_data2 = i->second;
}
else
{
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(block_txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << block_txid);
return 1;
}
tx_data2 = ::tx_data_t(tx);
if (opt_cache_txes)
state.tx_cache.insert(std::make_pair(block_txid, tx_data2));
}
for (size_t out = 0; out < tx_data2.vout.size(); ++out)
{
if (tx_data2.vout[out] == od.pubkey)
{
found = true;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid));
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid));
break;
}
}
}
if (!found)
{
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, output_txid));
}
}
}
@ -581,10 +610,6 @@ int main(int argc, char* argv[])
if (!txids.empty())
{
std::string stats_msg;
if (opt_show_cache_stats)
stats_msg = std::string(", cache: txes ") + std::to_string(cached_txes*100./total_txes)
+ ", blocks " + std::to_string(cached_blocks*100./total_blocks) + ", outputs "
+ std::to_string(cached_outputs*100./total_outputs);
MINFO("Height " << h << ": " << (block_ancestry_size / txids.size()) << " average over " << txids.size() << stats_msg);
}
state.height = h;
@ -608,14 +633,30 @@ int main(int argc, char* argv[])
}
state_data_out.close();
}
goto done;
}
else
{
if (state.height < db_height)
{
MWARNING("The state file is only built up to height " << state.height << ", but the blockchain reached height " << db_height);
MWARNING("You may want to run with --refresh if you want to get ancestry for newer data");
}
}
if (!opt_txid_string.empty())
{
start_txids.push_back(opt_txid);
}
else if (!opt_output_string.empty())
{
crypto::hash txid;
if (!get_output_txid(state, db, output_amount, output_offset, txid))
{
LOG_PRINT_L0("Output not found in db");
return 1;
}
start_txids.push_back(txid);
}
else
{
const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height);
@ -648,108 +689,40 @@ int main(int argc, char* argv[])
const crypto::hash txid = txids.front();
txids.pop_front();
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << txid);
if (stop_requested)
goto done;
::tx_data_t tx_data2;
if (!get_transaction(state, db, txid, tx_data2))
return 1;
}
const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
const bool coinbase = tx_data2.coinbase;
if (coinbase)
continue;
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
for (size_t ring = 0; ring < tx_data2.vin.size(); ++ring)
{
if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key))
{
const cryptonote::txin_to_key &txin = boost::get<cryptonote::txin_to_key>(tx.vin[ring]);
const uint64_t amount = txin.amount;
auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
const uint64_t amount = tx_data2.vin[ring].first;
auto absolute_offsets = tx_data2.vin[ring].second;
for (uint64_t offset: absolute_offsets)
{
add_ancestor(ancestry, amount, offset);
const output_data_t od = db->get_output_key(amount, offset);
bd = db->get_block_blob_from_height(od.height);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
// find the tx which created this output
bool found = false;
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx));
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return 1;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
if (found)
break;
if (!db->get_pruned_tx_blob(block_txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
return 1;
}
cryptonote::transaction tx2;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2))
{
LOG_PRINT_L0("Bad tx: " << block_txid);
return 1;
}
for (size_t out = 0; out < tx2.vout.size(); ++out)
{
if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(tx2.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
txids.push_back(block_txid);
MDEBUG("adding txid: " << block_txid);
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << block_txid);
return 1;
}
}
}
if (!found)
crypto::hash output_txid;
if (!get_output_txid(state, db, amount, offset, output_txid))
{
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, output_txid));
txids.push_back(output_txid);
MDEBUG("adding txid: " << output_txid);
}
}
else
{
LOG_PRINT_L0("Bad vin type in txid " << txid);
return 1;
}
}
}
@ -762,6 +735,13 @@ int main(int argc, char* argv[])
done:
core_storage->deinit();
if (opt_show_cache_stats)
MINFO("cache: txes " << std::to_string(cached_txes*100./total_txes)
<< "%, blocks " << std::to_string(cached_blocks*100./total_blocks)
<< "%, outputs " << std::to_string(cached_outputs*100./total_outputs)
<< "%");
return 0;
CATCH_ENTRY("Depth query error", 1);

@ -229,12 +229,15 @@ namespace
const char* USAGE_HELP("help");
const char* USAGE_HELP_ADVANCED("help_advanced [<command>]");
std::string input_line(const std::string& prompt)
std::string input_line(const std::string& prompt, bool yesno = false)
{
#ifdef HAVE_READLINE
rdln::suspend_readline pause_readline;
#endif
std::cout << prompt;
if (yesno)
std::cout << " (Y/Yes/N/No)";
std::cout << ": " << std::flush;
std::string buf;
#ifdef _WIN32
@ -424,10 +427,10 @@ namespace
<< ", " << dnssec_str << std::endl
<< sw::tr(" Wownero Address = ") << addresses[0]
<< std::endl
<< sw::tr("Is this OK? (Y/n) ")
<< sw::tr("Is this OK?")
;
// prompt the user for confirmation given the dns query and dnssec status
std::string confirm_dns_ok = input_line(prompt.str());
std::string confirm_dns_ok = input_line(prompt.str(), true);
if (std::cin.eof())
{
return {};
@ -595,7 +598,7 @@ namespace
fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
return false;
}
return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str()));
return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it?")) % filename).str(), true));
}
return true;
}
@ -3153,9 +3156,9 @@ bool simple_wallet::ask_wallet_create_if_needed()
LOG_PRINT_L3("User asked to specify wallet file name.");
wallet_path = input_line(
tr(m_restoring ? "Specify a new wallet file name for your restored wallet (e.g., MyWallet).\n"
"Wallet file name (or Ctrl-C to quit): " :
"Wallet file name (or Ctrl-C to quit)" :
"Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n"
"Wallet file name (or Ctrl-C to quit): ")
"Wallet file name (or Ctrl-C to quit)")
);
if(std::cin.eof())
{
@ -3202,7 +3205,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
if (!m_restoring)
{
message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path;
confirm_creation = input_line(tr("(Y/Yes/N/No): "));
confirm_creation = input_line("", true);
if(std::cin.eof())
{
LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
@ -3411,7 +3414,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_view_key;
// parse address
std::string address_string = input_line("Standard address: ");
std::string address_string = input_line("Standard address");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@ -3431,7 +3434,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse view secret key
epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
@ -3466,7 +3469,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_spend_key;
// parse spend secret key
epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: ");
epee::wipeable_string spendkey_string = input_secure_line("Secret spend key");
if (std::cin.eof())
return false;
if (spendkey_string.empty()) {
@ -3486,7 +3489,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_keys;
// parse address
std::string address_string = input_line("Standard address: ");
std::string address_string = input_line("Standard address");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@ -3506,7 +3509,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse spend secret key
epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: ");
epee::wipeable_string spendkey_string = input_secure_line("Secret spend key");
if (std::cin.eof())
return false;
if (spendkey_string.empty()) {
@ -3521,7 +3524,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse view secret key
epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
@ -3568,7 +3571,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
unsigned int multisig_n;
// parse multisig type
std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1): ");
std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1)");
if (std::cin.eof())
return false;
if (multisig_type_string.empty())
@ -3594,7 +3597,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n;
// parse multisig address
std::string address_string = input_line("Multisig wallet address: ");
std::string address_string = input_line("Multisig wallet address");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@ -3609,7 +3612,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse secret view key
epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
if (std::cin.eof())
return false;
if (viewkey_string.empty())
@ -3648,7 +3651,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
// get N secret spend keys from user
for(unsigned int i=0; i<multisig_n; ++i)
{
spendkey_string = input_secure_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+1) % multisig_m).str().c_str()));
spendkey_string = input_secure_line(tr((boost::format(tr("Secret spend key (%u of %u)")) % (i+1) % multisig_m).str().c_str()));
if (std::cin.eof())
return false;
if (spendkey_string.empty())
@ -3721,11 +3724,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if(m_wallet->get_refresh_from_block_height() == 0) {
{
tools::scoped_message_writer wrt = tools::msg_writer();
wrt << tr("No restore height is specified.");
wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height.");
wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height");
wrt << tr("No restore height is specified.") << " ";
wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height.") << " ";
wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height.");
}
std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): "));
std::string confirm = input_line(tr("Is this okay?"), true);
if (std::cin.eof() || !command_line::is_yes(confirm))
CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted"));
@ -3781,9 +3784,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
std::string heightstr;
if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
heightstr = input_line("Restore from specific blockchain height (optional, default 0): ");
heightstr = input_line("Restore from specific blockchain height (optional, default 0)");
else
heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD)");
if (std::cin.eof())
return false;
if (heightstr.empty())
@ -3812,7 +3815,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
success_msg_writer() << tr("Restore height is: ") << m_restore_height;
std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): "));
std::string confirm = input_line(tr("Is this okay?"), true);
if (std::cin.eof())
return false;
if(command_line::is_yes(confirm))
@ -3835,7 +3838,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_restore_height >= estimate_height)
{
success_msg_writer() << tr("Restore height ") << m_restore_height << (" is not yet reached. The current estimated height is ") << estimate_height;
std::string confirm = input_line(tr("Still apply restore height? (Y/Yes/N/No): "));
std::string confirm = input_line(tr("Still apply restore height?"), true);
if (std::cin.eof() || command_line::is_no(confirm))
m_restore_height = 0;
}
@ -3967,7 +3970,7 @@ std::string simple_wallet::get_mnemonic_language()
}
while (language_number < 0)
{
language_choice = input_line(tr("Enter the number corresponding to the language of your choice: "));
language_choice = input_line(tr("Enter the number corresponding to the language of your choice"));
if (std::cin.eof())
return std::string();
try
@ -5394,7 +5397,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
// prompt is there is no payment id and confirmation is required
if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
{
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway? (Y/Yes/N/No): "));
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway?"), true);
if (std::cin.eof())
return false;
if (!command_line::is_yes(accepted))
@ -5458,23 +5461,23 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog({std::make_pair(worst_fee_per_byte, worst_fee_per_byte)});
if (nblocks.size() != 1)
{
prompt << "Internal error checking for backlog. " << tr("Is this okay anyway? (Y/Yes/N/No): ");
prompt << "Internal error checking for backlog. " << tr("Is this okay anyway?");
}
else
{
if (nblocks[0].first > m_wallet->get_confirm_backlog_threshold())
prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): ")) % nblocks[0].first).str();
prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay?")) % nblocks[0].first).str();
}
}
catch (const std::exception &e)
{
prompt << tr("Failed to check for backlog: ") << e.what() << ENDL << tr("Is this okay anyway? (Y/Yes/N/No): ");
prompt << tr("Failed to check for backlog: ") << e.what() << ENDL << tr("Is this okay anyway?");
}
std::string prompt_str = prompt.str();
if (!prompt_str.empty())
{
std::string accepted = input_line(prompt_str);
std::string accepted = input_line(prompt_str, true);
if (std::cin.eof())
return false;
if (!command_line::is_yes(accepted))
@ -5560,9 +5563,9 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
{
prompt << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
}
prompt << ENDL << tr("Is this okay? (Y/Yes/N/No): ");
prompt << ENDL << tr("Is this okay?");
std::string accepted = input_line(prompt.str());
std::string accepted = input_line(prompt.str(), true);
if (std::cin.eof())
return false;
if (!command_line::is_yes(accepted))
@ -5700,17 +5703,17 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
std::string prompt_str = tr("Sweeping ") + print_money(total_unmixable);
if (ptx_vector.size() > 1) {
prompt_str = (boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
prompt_str = (boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
print_money(total_unmixable) %
((unsigned long long)ptx_vector.size()) %
print_money(total_fee)).str();
}
else {
prompt_str = (boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
prompt_str = (boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
print_money(total_unmixable) %
print_money(total_fee)).str();
}
std::string accepted = input_line(prompt_str);
std::string accepted = input_line(prompt_str, true);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@ -5753,7 +5756,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
catch (const tools::error::not_enough_unlocked_money& e)
{
fail_msg_writer() << tr("Not enough money in unlocked balance");
std::string accepted = input_line((boost::format(tr("Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay? (Y/Yes/N/No): ")) % print_money(e.available())).str());
std::string accepted = input_line((boost::format(tr("Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay?")) % print_money(e.available())).str(), true);
if (std::cin.eof())
return true;
if (command_line::is_yes(accepted))
@ -5961,7 +5964,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
// prompt is there is no payment id and confirmation is required
if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
{
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway? (Y/Yes/N/No): "));
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway?"), true);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@ -6009,17 +6012,17 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
return true;
if (ptx_vector.size() > 1) {
prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
print_money(total_sent) %
((unsigned long long)ptx_vector.size()) %
print_money(total_fee);
}
else {
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
print_money(total_sent) %
print_money(total_fee);
}
std::string accepted = input_line(prompt.str());
std::string accepted = input_line(prompt.str(), true);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@ -6229,7 +6232,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
// prompt if there is no payment id and confirmation is required
if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
{
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway? (Y/Yes/N/No): "));
std::string accepted = input_line(tr("There is no easy way for exchanges and large merchants to identify that this transaction came from you. Request a subaddress in these cases. Continue anyway?"), true);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@ -6271,10 +6274,10 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
std::ostringstream prompt;
if (!print_ring_members(ptx_vector, prompt))
return true;
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
print_money(total_sent) %
print_money(total_fee);
std::string accepted = input_line(prompt.str());
std::string accepted = input_line(prompt.str(), true);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@ -6552,8 +6555,8 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
change_string += tr("no change");
uint64_t fee = amount - amount_to_dests;
std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str();
return command_line::is_yes(input_line(prompt_str));
std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay?")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str();
return command_line::is_yes(input_line(prompt_str, true));
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
@ -7711,7 +7714,7 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
{
message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
std::string confirm = input_line(tr("Rescan anyway?"), true);
if(!std::cin.eof())
{
if (!command_line::is_yes(confirm))

@ -1683,7 +1683,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_txid = txid;
td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_requested = false;
if (!td.m_key_image_known)
{
// we might have cold signed, and have a mapping to key images
std::unordered_map<crypto::public_key, crypto::key_image>::const_iterator i = m_cold_key_images.find(tx_scan_info[o].in_ephemeral.pub);
if (i != m_cold_key_images.end())
{
td.m_key_image = i->second;
td.m_key_image_known = true;
}
}
if (m_watch_only)
{
// for view wallets, that flag means "we want to request it"
td.m_key_image_request = true;
}
else
{
td.m_key_image_request = false;
}
td.m_key_image_partial = m_multisig;
td.m_amount = amount;
td.m_pk_index = pk_index - 1;
@ -1705,7 +1723,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_rct = false;
}
set_unspent(m_transfers.size()-1);
if (!m_multisig && !m_watch_only)
if (td.m_key_image_known)
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
if (output_tracker_cache)
@ -5927,6 +5945,61 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
txs.back().additional_tx_keys = additional_tx_keys;
}
// add key image mapping for these txes
const account_keys &keys = get_account().get_keys();
hw::device &hwdev = m_account.get_device();
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
const cryptonote::transaction &tx = signed_txes.ptx[n].tx;
crypto::key_derivation derivation;
std::vector<crypto::key_derivation> additional_derivations;
// compute public keys from out secret keys
crypto::public_key tx_pub_key;
crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key);
std::vector<crypto::public_key> additional_tx_pub_keys;
for (const crypto::secret_key &skey: txs[n].additional_tx_keys)
{
additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
crypto::secret_key_to_public_key(skey, additional_tx_pub_keys.back());
}
// compute derivations
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
{
MWARNING("Failed to generate key derivation from tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
}
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
{
MWARNING("Failed to generate key derivation from additional tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
}
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
continue;
const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
// if this output is back to this wallet, we can calculate its key image already
if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
continue;
crypto::key_image ki;
cryptonote::keypair in_ephemeral;
if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
signed_txes.tx_key_images[out.key] = ki;
else
MERROR("Failed to calculate key image");
}
}
// add key images
signed_txes.key_images.resize(m_transfers.size());
for (size_t i = 0; i < m_transfers.size(); ++i)
@ -6089,6 +6162,10 @@ bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<too
bool r = import_key_images(signed_txs.key_images);
if (!r) return false;
// remember key images for this tx, for when we get those txes from the blockchain
for (const auto &e: signed_txs.tx_key_images)
m_cold_key_images.insert(e);
ptx = signed_txs.ptx;
return true;
@ -8293,7 +8370,7 @@ void wallet2::light_wallet_get_unspent_outs()
td.m_key_image = unspent_key_image;
td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_requested = false;
td.m_key_image_request = false;
td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
@ -10897,7 +10974,7 @@ std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>>
size_t offset = 0;
if (!all)
{
while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_requested)
while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
++offset;
}
@ -11057,7 +11134,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
m_transfers[n + offset].m_key_image = signed_key_images[n].first;
m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
m_transfers[n + offset].m_key_image_known = true;
m_transfers[n + offset].m_key_image_requested = false;
m_transfers[n + offset].m_key_image_request = false;
m_transfers[n + offset].m_key_image_partial = false;
}
PERF_TIMER_STOP(import_key_images_B);
@ -11276,7 +11353,7 @@ bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
td.m_key_image = key_images[i];
m_key_images[m_transfers[i].m_key_image] = i;
td.m_key_image_known = true;
td.m_key_image_requested = false;
td.m_key_image_request = false;
td.m_key_image_partial = false;
m_pub_keys[m_transfers[i].get_public_key()] = i;
}
@ -11348,7 +11425,7 @@ std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export
std::vector<tools::wallet2::transfer_details> outs;
size_t offset = 0;
while (offset < m_transfers.size() && m_transfers[offset].m_key_image_known)
while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
++offset;
outs.reserve(m_transfers.size() - offset);
@ -11392,7 +11469,7 @@ size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet
const size_t original_size = m_transfers.size();
m_transfers.resize(offset + outputs.second.size());
for (size_t i = 0; i < offset; ++i)
m_transfers[i].m_key_image_requested = false;
m_transfers[i].m_key_image_request = false;
for (size_t i = 0; i < outputs.second.size(); ++i)
{
transfer_details td = outputs.second[i];
@ -11433,7 +11510,7 @@ process:
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_requested = true;
td.m_key_image_request = true;
td.m_key_image_partial = false;
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
@ -11679,7 +11756,7 @@ void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key
m_key_images.erase(td.m_key_image);
td.m_key_image = get_multisig_composite_key_image(n);
td.m_key_image_known = true;
td.m_key_image_requested = false;
td.m_key_image_request = false;
td.m_key_image_partial = false;
td.m_multisig_k = multisig_k[n];
m_key_images[td.m_key_image] = n;

@ -267,7 +267,7 @@ namespace tools
uint64_t m_amount;
bool m_rct;
bool m_key_image_known;
bool m_key_image_requested;
bool m_key_image_request; // view wallets: we want to request it; cold wallets: it was requested
size_t m_pk_index;
cryptonote::subaddress_index m_subaddr_index;
bool m_key_image_partial;
@ -292,7 +292,7 @@ namespace tools
FIELD(m_amount)
FIELD(m_rct)
FIELD(m_key_image_known)
FIELD(m_key_image_requested)
FIELD(m_key_image_request)
FIELD(m_pk_index)
FIELD(m_subaddr_index)
FIELD(m_key_image_partial)
@ -448,6 +448,7 @@ namespace tools
{
std::vector<pending_tx> ptx;
std::vector<crypto::key_image> key_images;
std::unordered_map<crypto::public_key, crypto::key_image> tx_key_images;
};
struct multisig_tx_set
@ -927,6 +928,9 @@ namespace tools
if(ver < 27)
return;
a & m_device_last_key_image_sync;
if(ver < 28)
return;
a & m_cold_key_images;
}
/*!
@ -1358,6 +1362,7 @@ namespace tools
uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
std::unordered_map<crypto::public_key, crypto::key_image> m_cold_key_images;
std::atomic<bool> m_run;
@ -1452,7 +1457,7 @@ namespace tools
std::unique_ptr<wallet_device_callback> m_device_callback;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 27)
BOOST_CLASS_VERSION(tools::wallet2, 28)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
@ -1464,7 +1469,7 @@ BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 3)
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0)
@ -1513,7 +1518,7 @@ namespace boost
}
if (ver < 10)
{
x.m_key_image_requested = false;
x.m_key_image_request = false;
}
}
@ -1601,7 +1606,7 @@ namespace boost
initialize_transfer_details(a, x, ver);
return;
}
a & x.m_key_image_requested;
a & x.m_key_image_request;
if (ver < 11)
return;
a & x.m_uses;
@ -1801,6 +1806,9 @@ namespace boost
{
a & x.ptx;
a & x.key_images;
if (ver < 1)
return;
a & x.tx_key_images;
}
template <class Archive>

@ -50,6 +50,7 @@
#include "p2p/net_peerlist_boost_serialization.h"
#include "span.h"
#include "string_tools.h"
#include "storages/parserse_base_utils.h"
namespace
{
@ -833,3 +834,86 @@ TEST(net_buffer, move)
ASSERT_TRUE(!memcmp(span.data() + 1, std::string(4000, '0').c_str(), 4000));
}
TEST(parsing, isspace)
{
ASSERT_FALSE(epee::misc_utils::parse::isspace(0));
for (int c = 1; c < 256; ++c)
{
ASSERT_EQ(epee::misc_utils::parse::isspace(c), strchr("\r\n\t\f\v ", c) != NULL);
}
}
TEST(parsing, isdigit)
{
ASSERT_FALSE(epee::misc_utils::parse::isdigit(0));
for (int c = 1; c < 256; ++c)
{
ASSERT_EQ(epee::misc_utils::parse::isdigit(c), strchr("0123456789", c) != NULL);
}
}
TEST(parsing, number)
{
boost::string_ref val;
std::string s;
std::string::const_iterator i;
// the parser expects another character to end the number, and accepts things
// that aren't numbers, as it's meant as a pre-filter for strto* functions,
// so we just check that numbers get accepted, but don't test non numbers
s = "0 ";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "0");
s = "000 ";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "000");
s = "10x";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "10");
s = "10.09/";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "10.09");
s = "-1.r";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "-1.");
s = "-49.;";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "-49.");
s = "0.78/";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "0.78");
s = "33E9$";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "33E9");
s = ".34e2=";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, ".34e2");
s = "-9.34e-2=";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "-9.34e-2");
s = "+9.34e+03=";
i = s.begin();
epee::misc_utils::parse::match_number(i, s.end(), val);
ASSERT_EQ(val, "+9.34e+03");
}

@ -89,7 +89,7 @@ public:
virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; }
virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; }
virtual uint64_t get_indexing_base() const { return 0; }
virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const { return cryptonote::output_data_t(); }
virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const { return cryptonote::output_data_t(); }
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); }
virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); }
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {}

Loading…
Cancel
Save