Merge commit '2287fb9' into CherryPickUnboundWindowsBuildFix

This commit is contained in:
doy-lee 2018-10-22 15:43:00 +11:00
commit 913b3a40b5
54 changed files with 468 additions and 114 deletions

View file

@ -885,7 +885,7 @@ endif()
include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
if(MINGW) if(MINGW)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32) set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
if(DEPENDS) if(DEPENDS)
set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv) set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv)
else() else()

View file

@ -388,7 +388,7 @@ Then you can run make as usual.
# Get binaries # Get binaries
docker cp loki-android:/src/build/release/bin . docker cp loki-android:/src/build/release/bin .
### Building portable statically linked binaries (Cross Compiling) ### Building portable statically linked binaries
By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets: By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets:
@ -400,6 +400,68 @@ By default, in either dynamically or statically linked builds, binaries target t
* ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems * ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems
* ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems * ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems
### Cross Compiling
You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system. Go to `contrib/depends` and type:
* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries.
* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc
* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev
* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc
* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686
* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf
The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names.
Then go back to the source dir and type:
* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends/<chosen triplet>/share/toolchain.cmake```
Where <chosen triplet> is one of the above mentioned targets.
Using `depends` might also be easier to compile Monero on Windows than using MSys. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above.
## Installing Monero from a package
**DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.**
Packages are available for
* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build.
snap install monero --beta
Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released.
* Arch Linux (via [AUR](https://aur.archlinux.org/)):
- Stable release: [`monero`](https://aur.archlinux.org/packages/monero)
- Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git)
* Void Linux:
xbps-install -S monero
* GuixSD
guix package -i monero
* Docker
# Build using all available cores
docker build -t monero .
# or build using a specific number of cores (reduce RAM requirement)
docker build --build-arg NPROC=1 -t monero .
# either run in foreground
docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero
# or in background
docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero
* The build needs 3 GB space.
* Wait one hour or more
Packaging for your favorite distribution would be a welcome contribution!
## Running lokid ## Running lokid
The build places the binary in `bin/` sub-directory within the build directory The build places the binary in `bin/` sub-directory within the build directory

View file

@ -352,8 +352,11 @@ eof:
std::string command; std::string command;
bool get_line_ret = m_stdin_reader.get_line(command); bool get_line_ret = m_stdin_reader.get_line(command);
if (!m_running || m_stdin_reader.eos()) if (!m_running)
break;
if (m_stdin_reader.eos())
{ {
MGINFO("EOF on stdin, exiting");
break; break;
} }
if (!get_line_ret) if (!get_line_ret)

View file

@ -147,7 +147,8 @@ namespace misc_utils
{} {}
~call_befor_die() ~call_befor_die()
{ {
m_func(); try { m_func(); }
catch (...) { /* ignore */ }
} }
}; };

View file

@ -246,7 +246,6 @@ namespace net_utils
m_timer(io_serice) m_timer(io_serice)
{} {}
boost::asio::deadline_timer m_timer; boost::asio::deadline_timer m_timer;
uint64_t m_period;
}; };
template <class t_handler> template <class t_handler>
@ -262,25 +261,27 @@ namespace net_utils
{ {
return m_handler(); return m_handler();
} }
uint64_t m_period;
}; };
template<class t_handler> template<class t_handler>
bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms) bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms)
{ {
boost::shared_ptr<idle_callback_conext_base> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms)); boost::shared_ptr<idle_callback_conext<t_handler>> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms));
//needed call handler here ?... //needed call handler here ?...
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr)); ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler<t_handler>, this, ptr));
return true; return true;
} }
bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr<idle_callback_conext_base> ptr) template<class t_handler>
bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr<idle_callback_conext<t_handler>> ptr)
{ {
//if handler return false - he don't want to be called anymore //if handler return false - he don't want to be called anymore
if(!ptr->call_handler()) if(!ptr->call_handler())
return true; return true;
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr)); ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler<t_handler>, this, ptr));
return true; return true;
} }

View file

@ -92,7 +92,6 @@ class connection_basic { // not-templated base class for rapid developmet of som
critical_section m_send_que_lock; critical_section m_send_que_lock;
std::list<std::string> m_send_que; std::list<std::string> m_send_que;
volatile bool m_is_multithreaded; volatile bool m_is_multithreaded;
double m_start_time;
/// Strand to ensure the connection's handlers are not called concurrently. /// Strand to ensure the connection's handlers are not called concurrently.
boost::asio::io_service::strand strand_; boost::asio::io_service::strand strand_;
/// Socket for the connection. /// Socket for the connection.
@ -112,8 +111,6 @@ class connection_basic { // not-templated base class for rapid developmet of som
void logger_handle_net_write(size_t size); // network data written void logger_handle_net_write(size_t size); // network data written
void logger_handle_net_read(size_t size); // network data read void logger_handle_net_read(size_t size); // network data read
void set_start_time();
// config for rate limit // config for rate limit
static void set_rate_up_limit(uint64_t limit); static void set_rate_up_limit(uint64_t limit);

View file

@ -99,7 +99,7 @@ public:
size_t get_connections_count(); size_t get_connections_count();
void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL); void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
{} {}
~async_protocol_handler_config() { set_handler(NULL, NULL); } ~async_protocol_handler_config() { set_handler(NULL, NULL); }
void del_out_connections(size_t count); void del_out_connections(size_t count);
@ -272,6 +272,8 @@ public:
m_wait_count = 0; m_wait_count = 0;
m_oponent_protocol_ver = 0; m_oponent_protocol_ver = 0;
m_connection_initialized = false; m_connection_initialized = false;
m_invoke_buf_ready = 0;
m_invoke_result_code = LEVIN_ERROR_CONNECTION;
} }
virtual ~async_protocol_handler() virtual ~async_protocol_handler()
{ {

View file

@ -250,22 +250,15 @@ void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q
} }
} }
void connection_basic::set_start_time() {
CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out );
m_start_time = network_throttle_manager::get_global_throttle_out().get_time_seconds();
}
void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) { void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) {
// No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write // No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write
MTRACE("handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)"); MTRACE("handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)");
set_start_time();
} }
void connection_basic::do_send_handler_write_from_queue( const boost::system::error_code& e, size_t cb, int q_len ) { void connection_basic::do_send_handler_write_from_queue( const boost::system::error_code& e, size_t cb, int q_len ) {
// No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write // No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write
MTRACE("handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)"); MTRACE("handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)");
set_start_time();
} }
void connection_basic::logger_handle_net_read(size_t size) { // network data read void connection_basic::logger_handle_net_read(size_t size) { // network data read

View file

@ -28,6 +28,13 @@
#ifndef _MLOG_H_ #ifndef _MLOG_H_
#define _MLOG_H_ #define _MLOG_H_
#ifdef _WIN32
#include <windows.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#endif
#include <time.h> #include <time.h>
#include <atomic> #include <atomic>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -117,6 +124,31 @@ static const char *get_default_categories(int level)
return categories; return categories;
} }
#ifdef WIN32
bool EnableVTMode()
{
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
{
return false;
}
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
{
return false;
}
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode))
{
return false;
}
return true;
}
#endif
void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files) void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files)
{ {
el::Configurations c; el::Configurations c;
@ -202,6 +234,9 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
loki_log = get_default_categories(0); loki_log = get_default_categories(0);
} }
mlog_set_log(loki_log); mlog_set_log(loki_log);
#ifdef WIN32
EnableVTMode();
#endif
} }
void mlog_set_categories(const char *categories) void mlog_set_categories(const char *categories)

View file

@ -146,6 +146,7 @@ network_throttle::network_throttle(const std::string &nameshort, const std::stri
m_network_add_cost = 128; m_network_add_cost = 128;
m_network_minimal_segment = 256; m_network_minimal_segment = 256;
m_network_max_segment = 1024*1024; m_network_max_segment = 1024*1024;
m_start_time = 0;
m_any_packet_yet = false; m_any_packet_yet = false;
m_slot_size = 1.0; // hard coded in few places m_slot_size = 1.0; // hard coded in few places
m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle

View file

@ -183,7 +183,7 @@ target_link_libraries(blockchain_blackball
set_property(TARGET blockchain_blackball set_property(TARGET blockchain_blackball
PROPERTY PROPERTY
OUTPUT_NAME "loki-blockchain-blackball") OUTPUT_NAME "loki-blockchain-mark-spent-outputs")
install(TARGETS blockchain_blackball DESTINATION bin) install(TARGETS blockchain_blackball DESTINATION bin)

View file

@ -227,7 +227,7 @@ static void init(std::string cache_filename)
bool tx_active = false; bool tx_active = false;
int dbr; int dbr;
MINFO("Creating blackball cache in " << cache_filename); MINFO("Creating spent output cache in " << cache_filename);
tools::create_directories_if_necessary(cache_filename); tools::create_directories_if_necessary(cache_filename);
@ -1020,7 +1020,7 @@ int main(int argc, char* argv[])
po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options"); po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_blackball_db_dir = { const command_line::arg_descriptor<std::string> arg_blackball_db_dir = {
"blackball-db-dir", "Specify blackball database directory", "spent-output-db-dir", "Specify spent output database directory",
get_default_db_path(), get_default_db_path(),
}; };
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
@ -1077,7 +1077,7 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
mlog_configure(mlog_get_default_log_path("loki-blockchain-blackball.log"), true); mlog_configure(mlog_get_default_log_path("loki-blockchain-find-spent-outputs.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level)) if (!command_line::is_arg_defaulted(vm, arg_log_level))
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
else else
@ -1134,10 +1134,10 @@ int main(int argc, char* argv[])
core_storage[n] = &(blockchain_objects->m_blockchain); core_storage[n] = &(blockchain_objects->m_blockchain);
} }
const std::string cache_dir = (output_file_path / "blackball-cache").string(); const std::string cache_dir = (output_file_path / "spent-outputs-cache").string();
init(cache_dir); init(cache_dir);
LOG_PRINT_L0("Scanning for blackballable outputs..."); LOG_PRINT_L0("Scanning for spent outputs...");
size_t done = 0; size_t done = 0;
@ -1235,7 +1235,7 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]); const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]);
if (opt_verbose) if (opt_verbose)
{ {
MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a 1-ring"); MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a 1-ring");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
} }
blackballs.push_back(output); blackballs.push_back(output);
@ -1249,7 +1249,7 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]); const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
if (opt_verbose) if (opt_verbose)
{ {
MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
} }
blackballs.push_back(output); blackballs.push_back(output);
@ -1264,7 +1264,7 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]); const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
if (opt_verbose) if (opt_verbose)
{ {
MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
} }
blackballs.push_back(output); blackballs.push_back(output);
@ -1300,7 +1300,7 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]); const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]);
if (opt_verbose) if (opt_verbose)
{ {
MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in rings with a single common element"); MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in rings with a single common element");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
} }
blackballs.push_back(output); blackballs.push_back(output);
@ -1412,7 +1412,7 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(od.amount, last_unknown); const std::pair<uint64_t, uint64_t> output = std::make_pair(od.amount, last_unknown);
if (opt_verbose) if (opt_verbose)
{ {
MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a " << MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a " <<
absolute.size() << "-ring where all other outputs are known to be spent"); absolute.size() << "-ring where all other outputs are known to be spent");
} }
blackballs.push_back(output); blackballs.push_back(output);
@ -1440,7 +1440,7 @@ int main(int argc, char* argv[])
skip_secondary_passes: skip_secondary_passes:
uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs; uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs;
LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs() << " total outputs blackballed"); LOG_PRINT_L0(std::to_string(diff) << " new outputs marked as spent, " << get_num_spent_outputs() << " total outputs marked as spent");
MDB_txn *txn; MDB_txn *txn;
dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
@ -1480,7 +1480,7 @@ skip_secondary_passes:
mdb_txn_abort(txn); mdb_txn_abort(txn);
} }
LOG_PRINT_L0("Blockchain blackball data exported OK"); LOG_PRINT_L0("Blockchain spent output data exported OK");
close_db(env0, txn0, cur0, dbi0); close_db(env0, txn0, cur0, dbi0);
close(); close();
return 0; return 0;

View file

@ -350,7 +350,9 @@ public:
using error_type = std::error_code; using error_type = std::error_code;
//! Create a successful object. //! Create a successful object.
expect() = default; expect() noexcept
: code_()
{}
expect(std::error_code const& code) noexcept expect(std::error_code const& code) noexcept
: code_(code) : code_(code)

View file

@ -38,6 +38,7 @@
#endif #endif
#include "misc_log_ex.h" #include "misc_log_ex.h"
#include "util.h"
#include "spawn.h" #include "spawn.h"
namespace tools namespace tools
@ -101,6 +102,8 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait)
// child // child
if (pid == 0) if (pid == 0)
{ {
tools::closefrom(3);
close(0);
char *envp[] = {NULL}; char *envp[] = {NULL};
execve(filename, argv, envp); execve(filename, argv, envp);
MERROR("Failed to execve: " << strerror(errno)); MERROR("Failed to execve: " << strerror(errno));

View file

@ -49,11 +49,19 @@ threadpool::~threadpool() {
} }
void threadpool::stop() { void threadpool::stop() {
try
{ {
const boost::unique_lock<boost::mutex> lock(mutex); const boost::unique_lock<boost::mutex> lock(mutex);
running = false; running = false;
has_work.notify_all(); has_work.notify_all();
} }
catch (...)
{
// if the lock throws, we're just do it without a lock and hope,
// since the alternative is terminate
running = false;
has_work.notify_all();
}
for (size_t i = 0; i<threads.size(); i++) { for (size_t i = 0; i<threads.size(); i++) {
try { threads[i].join(); } try { threads[i].join(); }
catch (...) { /* ignore */ } catch (...) { /* ignore */ }
@ -103,11 +111,13 @@ unsigned int threadpool::get_max_concurrency() const {
threadpool::waiter::~waiter() threadpool::waiter::~waiter()
{ {
try
{ {
boost::unique_lock<boost::mutex> lock(mt); boost::unique_lock<boost::mutex> lock(mt);
if (num) if (num)
MERROR("wait should have been called before waiter dtor - waiting now"); MERROR("wait should have been called before waiter dtor - waiting now");
} }
catch (...) { /* ignore */ }
try try
{ {
wait(NULL); wait(NULL);

View file

@ -29,6 +29,7 @@
// //
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <unistd.h>
#include <cstdio> #include <cstdio>
#ifdef __GLIBC__ #ifdef __GLIBC__
@ -234,7 +235,7 @@ namespace tools
MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
} }
#else #else
m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666); m_fd = open(filename.c_str(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666);
if (m_fd != -1) if (m_fd != -1)
{ {
if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) if (flock(m_fd, LOCK_EX | LOCK_NB) == -1)
@ -968,4 +969,23 @@ std::string get_nix_version_display_string()
} }
#endif #endif
void closefrom(int fd)
{
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __DragonFly__
::closefrom(fd);
#else
#if defined __GLIBC__
const int sc_open_max = sysconf(_SC_OPEN_MAX);
const int MAX_FDS = std::min(65536, sc_open_max);
#else
const int MAX_FDS = 65536;
#endif
while (fd < MAX_FDS)
{
close(fd);
++fd;
}
#endif
}
} }

View file

@ -238,4 +238,6 @@ namespace tools
#ifdef _WIN32 #ifdef _WIN32
std::string input_line_win(); std::string input_line_win();
#endif #endif
void closefrom(int fd);
} }

View file

@ -200,7 +200,7 @@ namespace cryptonote
float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size());
const auto flags = std::cout.flags(); const auto flags = std::cout.flags();
const auto precision = std::cout.precision(); const auto precision = std::cout.precision();
std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << flags << precision << ENDL; std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << std::setiosflags(flags) << std::setprecision(precision) << ENDL;
} }
} }
m_last_hr_merge_time = misc_utils::get_tick_count(); m_last_hr_merge_time = misc_utils::get_tick_count();

View file

@ -306,6 +306,9 @@ uint64_t Blockchain::get_current_blockchain_height() const
bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CHECK_AND_ASSERT_MES(nettype != FAKECHAIN || test_options, false, "fake chain network type used without options");
CRITICAL_REGION_LOCAL(m_tx_pool); CRITICAL_REGION_LOCAL(m_tx_pool);
CRITICAL_REGION_LOCAL1(m_blockchain_lock); CRITICAL_REGION_LOCAL1(m_blockchain_lock);
@ -2386,7 +2389,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{ {
MERROR("Bulletproofs are not allowed before v10"); MERROR_VER("Bulletproofs are not allowed before v10");
tvc.m_invalid_output = true; tvc.m_invalid_output = true;
return false; return false;
} }
@ -2396,7 +2399,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type); const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type);
if (borromean) if (borromean)
{ {
MERROR("Borromean range proofs are not allowed after v10"); MERROR_VER("Borromean range proofs are not allowed after v10");
tvc.m_invalid_output = true; tvc.m_invalid_output = true;
return false; return false;
} }

View file

@ -918,16 +918,19 @@ namespace cryptonote
} }
waiter.wait(&tpool); waiter.wait(&tpool);
it = tx_blobs.begin(); it = tx_blobs.begin();
std::vector<bool> already_have(tx_blobs.size(), false);
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res) if (!results[i].res)
continue; continue;
if(m_mempool.have_tx(results[i].hash)) if(m_mempool.have_tx(results[i].hash))
{ {
LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool"); LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
already_have[i] = true;
} }
else if(m_blockchain_storage.have_tx(results[i].hash)) else if(m_blockchain_storage.have_tx(results[i].hash))
{ {
LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain"); LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
already_have[i] = true;
} }
else else
{ {
@ -949,7 +952,7 @@ namespace cryptonote
std::vector<tx_verification_batch_info> tx_info; std::vector<tx_verification_batch_info> tx_info;
tx_info.reserve(tx_blobs.size()); tx_info.reserve(tx_blobs.size());
for (size_t i = 0; i < tx_blobs.size(); i++) { for (size_t i = 0; i < tx_blobs.size(); i++) {
if (!results[i].res) if (!results[i].res || already_have[i])
continue; continue;
tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res}); tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res});
} }
@ -959,6 +962,8 @@ namespace cryptonote
bool ok = true; bool ok = true;
it = tx_blobs.begin(); it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (already_have[i])
continue;
if (!results[i].res) if (!results[i].res)
{ {
ok = false; ok = false;

View file

@ -887,7 +887,7 @@ namespace cryptonote
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
tx_info txi; tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid); txi.id_hash = epee::string_tools::pod_to_hex(txid);
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(*bd); txi.tx_blob = *bd;
transaction tx; transaction tx;
if (!parse_and_validate_tx_from_blob(*bd, tx)) if (!parse_and_validate_tx_from_blob(*bd, tx))
{ {

View file

@ -138,13 +138,18 @@ bool t_daemon::run(bool interactive)
throw std::runtime_error{"Can't run stopped daemon"}; throw std::runtime_error{"Can't run stopped daemon"};
} }
std::atomic<bool> stop(false); std::atomic<bool> stop(false), shutdown(false);
boost::thread([&stop, this] { boost::thread stop_thread = boost::thread([&stop, &shutdown, this] {
while (!stop) while (!stop)
epee::misc_utils::sleep_no_w(100); epee::misc_utils::sleep_no_w(100);
this->stop_p2p(); if (shutdown)
}).detach(); this->stop_p2p();
tools::signal_handler::install([&stop](int){ stop = true; }); });
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
stop = true;
stop_thread.join();
});
tools::signal_handler::install([&stop, &shutdown](int){ stop = shutdown = true; });
try try
{ {

View file

@ -32,6 +32,7 @@
#include "common/util.h" #include "common/util.h"
#include "daemonizer/windows_service.h" #include "daemonizer/windows_service.h"
#include "daemonizer/windows_service_runner.h" #include "daemonizer/windows_service_runner.h"
#include "cryptonote_core/cryptonote_core.h"
#include <shlobj.h> #include <shlobj.h>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>

View file

@ -13,6 +13,7 @@
// //
#if defined(HAVE_HIDAPI) #if defined(HAVE_HIDAPI)
#include <boost/scope_exit.hpp>
#include "log.hpp" #include "log.hpp"
#include "device_io_hid.hpp" #include "device_io_hid.hpp"
@ -69,11 +70,47 @@ namespace hw {
void device_io_hid::connect(void *params) { void device_io_hid::connect(void *params) {
hid_conn_params *p = (struct hid_conn_params*)params; hid_conn_params *p = (struct hid_conn_params*)params;
this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page); this->connect(p->vid, p->pid, p->interface_number, p->usage_page);
} }
void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) { hid_device_info *device_io_hid::find_device(hid_device_info *devices_list, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
hid_device_info *hwdev_info, *hwdev_info_list; bool select_any = !interface_number && !usage_page;
MDEBUG( "Looking for " <<
(select_any ? "any HID Device" : "HID Device with") <<
(interface_number ? (" interface_number " + std::to_string(interface_number.value())) : "") <<
((interface_number && usage_page) ? " or" : "") <<
(usage_page ? (" usage_page " + std::to_string(usage_page.value())) : ""));
hid_device_info *result = nullptr;
for (; devices_list != nullptr; devices_list = devices_list->next) {
BOOST_SCOPE_EXIT(&devices_list, &result) {
MDEBUG( (result == devices_list ? "SELECTED" : "SKIPPED ") <<
" HID Device" <<
" path " << safe_hid_path(devices_list) <<
" interface_number " << devices_list->interface_number <<
" usage_page " << devices_list->usage_page);
}
BOOST_SCOPE_EXIT_END
if (result != nullptr) {
continue;
}
if (select_any) {
result = devices_list;
} else if (interface_number && devices_list->interface_number == interface_number.value()) {
result = devices_list;
} else if (usage_page && devices_list->usage_page == usage_page.value()) {
result = devices_list;
}
}
return result;
}
void device_io_hid::connect(unsigned int vid, unsigned int pid, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
hid_device_info *hwdev_info_list;
hid_device *hwdev; hid_device *hwdev;
this->disconnect(); this->disconnect();
@ -81,17 +118,8 @@ namespace hw {
hwdev_info_list = hid_enumerate(vid, pid); hwdev_info_list = hid_enumerate(vid, pid);
ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device)); ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device));
hwdev = NULL; hwdev = NULL;
hwdev_info = hwdev_info_list; if (hid_device_info *device = find_device(hwdev_info_list, interface_number, usage_page)) {
while (hwdev_info) { hwdev = hid_open_path(device->path);
if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) ||
((usage_page == 0xffa0) && (interface_number == 0)) ) {
MDEBUG("HID Device found: " << safe_hid_path(hwdev_info));
hwdev = hid_open_path(hwdev_info->path);
break;
} else {
MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")");
}
hwdev_info = hwdev_info->next;
} }
hid_free_enumeration(hwdev_info_list); hid_free_enumeration(hwdev_info_list);
ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid)); ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid));

View file

@ -29,6 +29,7 @@
#if defined(HAVE_HIDAPI) #if defined(HAVE_HIDAPI)
#include <boost/optional/optional.hpp>
#include <hidapi/hidapi.h> #include <hidapi/hidapi.h>
#include "device_io.hpp" #include "device_io.hpp"
@ -52,9 +53,8 @@ namespace hw {
struct hid_conn_params { struct hid_conn_params {
unsigned int vid; unsigned int vid;
unsigned int pid; unsigned int pid;
unsigned int interface_number; int interface_number;
unsigned int usage_page; unsigned short usage_page;
bool interface_OR_page ;
}; };
@ -82,13 +82,11 @@ namespace hw {
unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len); unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len);
unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len); unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len);
hid_device_info *find_device(hid_device_info *devices_list, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page);
public: public:
bool hid_verbose = false; bool hid_verbose = false;
static const unsigned int OR_SELECT = 1;
static const unsigned int AND_SELECT = 2;
static const unsigned short DEFAULT_CHANNEL = 0x0001; static const unsigned short DEFAULT_CHANNEL = 0x0001;
static const unsigned char DEFAULT_TAG = 0x01; static const unsigned char DEFAULT_TAG = 0x01;
static const unsigned int DEFAULT_PACKET_SIZE = 64; static const unsigned int DEFAULT_PACKET_SIZE = 64;
@ -100,7 +98,7 @@ namespace hw {
void init(); void init();
void connect(void *params); void connect(void *params);
void connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ); void connect(unsigned int vid, unsigned int pid, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page);
bool connected() const; bool connected() const;
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len); int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
void disconnect(); void disconnect();

View file

@ -341,7 +341,7 @@ namespace hw {
bool device_ledger::connect(void) { bool device_ledger::connect(void) {
this->disconnect(); this->disconnect();
hw_device.connect(0x2c97,0x0001, 0, 0xffa0, hw_device.OR_SELECT); hw_device.connect(0x2c97, 0x0001, 0, 0xffa0);
this->reset(); this->reset();
#ifdef DEBUG_HWDEVICE #ifdef DEBUG_HWDEVICE
cryptonote::account_public_address pubkey; cryptonote::account_public_address pubkey;

View file

@ -918,6 +918,8 @@ namespace cryptonote
return r; return r;
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted); m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
for (tx_info& txi : res.transactions)
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
return true; return true;
} }
@ -1084,7 +1086,7 @@ namespace cryptonote
{ {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template"; error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to tx pub key in coinbase extra"); LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false; return false;
} }
res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
@ -2164,6 +2166,8 @@ namespace cryptonote
try try
{ {
// 0 is placeholder for the whole chain
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts) for (uint64_t amount: req.amounts)
{ {
static struct D static struct D
@ -2176,7 +2180,7 @@ namespace cryptonote
} d; } d;
boost::unique_lock<boost::mutex> lock(d.mutex); boost::unique_lock<boost::mutex> lock(d.mutex);
if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req.to_height) if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height)
{ {
res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base}); res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base});
if (!req.cumulative) if (!req.cumulative)
@ -2191,23 +2195,23 @@ namespace cryptonote
std::vector<uint64_t> distribution; std::vector<uint64_t> distribution;
uint64_t start_height, base; uint64_t start_height, base;
if (!m_core.get_output_distribution(amount, req.from_height, req.to_height, start_height, distribution, base)) if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base))
{ {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Failed to get rct distribution"; error_resp.message = "Failed to get rct distribution";
return false; return false;
} }
if (req.to_height > 0 && req.to_height >= req.from_height) if (req_to_height > 0 && req_to_height >= req.from_height)
{ {
uint64_t offset = std::max(req.from_height, start_height); uint64_t offset = std::max(req.from_height, start_height);
if (offset <= req.to_height && req.to_height - offset + 1 < distribution.size()) if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size())
distribution.resize(req.to_height - offset + 1); distribution.resize(req_to_height - offset + 1);
} }
if (amount == 0) if (amount == 0)
{ {
d.cached_from = req.from_height; d.cached_from = req.from_height;
d.cached_to = req.to_height; d.cached_to = req_to_height;
d.cached_distribution = distribution; d.cached_distribution = distribution;
d.cached_start_height = start_height; d.cached_start_height = start_height;
d.cached_base = base; d.cached_base = base;

View file

@ -67,7 +67,7 @@ class classname \
// NOTE: when using a type with multiple template parameters, // NOTE: when using a type with multiple template parameters,
// replace any comma in the template specifier with the macro // replace any comma in the template specifier with the macro
// above, or the preprocessor will eat the comma in a bad way. // above, or the preprocessor will eat the comma in a bad way.
#define RPC_MESSAGE_MEMBER(type, name) type name; #define RPC_MESSAGE_MEMBER(type, name) type name = type{}
namespace cryptonote namespace cryptonote

View file

@ -1664,7 +1664,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets; uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
if (args.size() == 0) if (args.size() == 0)
{ {
fail_msg_writer() << tr("usage: blackball <amount>/<offset> | <filename> [add]"); fail_msg_writer() << tr("usage: mark_output_spent <amount>/<offset> | <filename> [add]");
return true; return true;
} }
@ -1742,7 +1742,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
fail_msg_writer() << tr("Failed to blackball output: ") << e.what(); fail_msg_writer() << tr("Failed to mark output spent: ") << e.what();
} }
return true; return true;
@ -1753,7 +1753,7 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args)
std::pair<uint64_t, uint64_t> output; std::pair<uint64_t, uint64_t> output;
if (args.size() != 1) if (args.size() != 1)
{ {
fail_msg_writer() << tr("usage: unblackball <amount>/<offset>"); fail_msg_writer() << tr("usage: mark_output_unspent <amount>/<offset>");
return true; return true;
} }
@ -1769,7 +1769,7 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args)
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); fail_msg_writer() << tr("Failed to mark output unspent: ") << e.what();
} }
return true; return true;
@ -1780,7 +1780,7 @@ bool simple_wallet::blackballed(const std::vector<std::string> &args)
std::pair<uint64_t, uint64_t> output; std::pair<uint64_t, uint64_t> output;
if (args.size() != 1) if (args.size() != 1)
{ {
fail_msg_writer() << tr("usage: blackballed <amount>/<offset>"); fail_msg_writer() << tr("usage: is_output_spent <amount>/<offset>");
return true; return true;
} }
@ -1793,13 +1793,13 @@ bool simple_wallet::blackballed(const std::vector<std::string> &args)
try try
{ {
if (m_wallet->is_output_blackballed(output)) if (m_wallet->is_output_blackballed(output))
message_writer() << tr("Blackballed: ") << output.first << "/" << output.second; message_writer() << tr("Spent: ") << output.first << "/" << output.second;
else else
message_writer() << tr("not blackballed: ") << output.first << "/" << output.second; message_writer() << tr("Not spent: ") << output.first << "/" << output.second;
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); fail_msg_writer() << tr("Failed to check whether output is spent: ") << e.what();
} }
return true; return true;
@ -2589,18 +2589,18 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::save_known_rings, this, _1), boost::bind(&simple_wallet::save_known_rings, this, _1),
tr("save_known_rings"), tr("save_known_rings"),
tr("Save known rings to the shared rings database")); tr("Save known rings to the shared rings database"));
m_cmd_binder.set_handler("blackball", m_cmd_binder.set_handler("mark_output_spent",
boost::bind(&simple_wallet::blackball, this, _1), boost::bind(&simple_wallet::blackball, this, _1),
tr("blackball <amount>/<offset> | <filename> [add]"), tr("mark_output_spent <amount>/<offset> | <filename> [add]"),
tr("Blackball output(s) so they never get selected as fake outputs in a ring")); tr("Mark output(s) as spent so they never get selected as fake outputs in a ring"));
m_cmd_binder.set_handler("unblackball", m_cmd_binder.set_handler("mark_output_unspent",
boost::bind(&simple_wallet::unblackball, this, _1), boost::bind(&simple_wallet::unblackball, this, _1),
tr("unblackball <amount>/<offset>"), tr("mark_output_unspent <amount>/<offset>"),
tr("Unblackballs an output so it may get selected as a fake output in a ring")); tr("Marks an output as unspent so it may get selected as a fake output in a ring"));
m_cmd_binder.set_handler("blackballed", m_cmd_binder.set_handler("is_output_spent",
boost::bind(&simple_wallet::blackballed, this, _1), boost::bind(&simple_wallet::blackballed, this, _1),
tr("blackballed <amount>/<offset>"), tr("is_output_spent <amount>/<offset>"),
tr("Checks whether an output is blackballed")); tr("Checks whether an output is marked as spent"));
m_cmd_binder.set_handler("version", m_cmd_binder.set_handler("version",
boost::bind(&simple_wallet::version, this, _1), boost::bind(&simple_wallet::version, this, _1),
tr("version"), tr("version"),

View file

@ -777,7 +777,7 @@ bool WalletImpl::setPassword(const std::string &password)
{ {
clearStatus(); clearStatus();
try { try {
m_wallet->rewrite(m_wallet->get_wallet_file(), password); m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password);
m_password = password; m_password = password;
} catch (const std::exception &e) { } catch (const std::exception &e) {
setStatusError(e.what()); setStatusError(e.what());
@ -2162,7 +2162,7 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool
bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add); bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add);
if (!ret) if (!ret)
{ {
setStatusError(tr("Failed to set blackballed outputs")); setStatusError(tr("Failed to mark outputs as spent"));
return false; return false;
} }
return true; return true;
@ -2184,7 +2184,7 @@ bool WalletImpl::blackballOutput(const std::string &amount, const std::string &o
bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset)); bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret) if (!ret)
{ {
setStatusError(tr("Failed to blackball output")); setStatusError(tr("Failed to mark output as spent"));
return false; return false;
} }
return true; return true;
@ -2206,7 +2206,7 @@ bool WalletImpl::unblackballOutput(const std::string &amount, const std::string
bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset)); bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret) if (!ret)
{ {
setStatusError(tr("Failed to unblackball output")); setStatusError(tr("Failed to mark output as unspent"));
return false; return false;
} }
return true; return true;

View file

@ -413,13 +413,13 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
switch (op) switch (op)
{ {
case BLACKBALL_BLACKBALL: case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output.first << "/" << output.second); MDEBUG("Marking output " << output.first << "/" << output.second << " as spent");
dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP); dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP);
if (dbr == MDB_KEYEXIST) if (dbr == MDB_KEYEXIST)
dbr = 0; dbr = 0;
break; break;
case BLACKBALL_UNBLACKBALL: case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output.first << "/" << output.second); MDEBUG("Marking output " << output.first << "/" << output.second << " as unspent");
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
if (dbr == 0) if (dbr == 0)
dbr = mdb_cursor_del(cursor, 0); dbr = mdb_cursor_del(cursor, 0);

View file

@ -38,6 +38,8 @@
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "include_base_utils.h" #include "include_base_utils.h"
using namespace epee; using namespace epee;
@ -6979,6 +6981,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount)); LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount));
} }
std::unordered_map<const char*, std::set<uint64_t>> picks;
// while we still need more mixins // while we still need more mixins
uint64_t num_usable_outs = num_outs; uint64_t num_usable_outs = num_outs;
bool allow_blackballed = false; bool allow_blackballed = false;
@ -6993,7 +6997,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// outputs, we still need to reach the minimum ring size) // outputs, we still need to reach the minimum ring size)
if (allow_blackballed) if (allow_blackballed)
break; break;
MINFO("Not enough non blackballed outputs, we'll allow blackballed ones"); MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
allow_blackballed = true; allow_blackballed = true;
num_usable_outs = num_outs; num_usable_outs = num_outs;
} }
@ -7077,11 +7081,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
} }
seen_indices.emplace(i); seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type); picks[type].insert(i);
req.outputs.push_back({amount, i}); req.outputs.push_back({amount, i});
++num_found; ++num_found;
} }
for (const auto &pick: picks)
MDEBUG("picking " << pick.first << " outputs: " <<
boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
// if we had enough unusable outputs, we might fall off here and still // if we had enough unusable outputs, we might fall off here and still
// have too few outputs, so we stuff with one to keep counts good, and // have too few outputs, so we stuff with one to keep counts good, and
// we'll error out later // we'll error out later
@ -7097,8 +7105,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
[](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
} }
for (auto i: req.outputs) if (ELPP->vRegistry()->allowed(el::Level::Debug, LOKI_DEFAULT_LOG_CATEGORY))
LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount)); {
std::map<uint64_t, std::set<uint64_t>> outs;
for (const auto &i: req.outputs)
outs[i.amount].insert(i.index);
for (const auto &o: outs)
MDEBUG("asking for outputs with amount " << print_money(o.first) << ": " <<
boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
}
// get the keys for those // get the keys for those
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();

View file

@ -2823,8 +2823,7 @@ namespace tools
{ {
try try
{ {
m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password); m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
m_wallet->store();
LOG_PRINT_L0("Wallet password changed."); LOG_PRINT_L0("Wallet password changed.");
} }
catch (const std::exception& e) catch (const std::exception& e)

View file

@ -35,6 +35,7 @@
#include <vector> #include <vector>
#include "warnings.h" #include "warnings.h"
#include "misc_log_ex.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "crypto/hash.h" #include "crypto/hash.h"
#include "crypto-tests.h" #include "crypto-tests.h"
@ -59,6 +60,7 @@ bool operator !=(const key_derivation &a, const key_derivation &b) {
DISABLE_GCC_WARNING(maybe-uninitialized) DISABLE_GCC_WARNING(maybe-uninitialized)
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
TRY_ENTRY();
fstream input; fstream input;
string cmd; string cmd;
size_t test = 0; size_t test = 0;
@ -266,4 +268,5 @@ error:
error = true; error = true;
} }
return error ? 1 : 0; return error ? 1 : 0;
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -26,10 +26,6 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # 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. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if(WIN32)
set(EXTRA_LIBRARIES "${EXTRA_LIBRARIES};bcrypt")
endif()
set(functional_tests_sources set(functional_tests_sources
main.cpp main.cpp
transactions_flow_test.cpp transactions_flow_test.cpp

View file

@ -68,7 +68,9 @@ int Base58Fuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
Base58Fuzzer fuzzer; Base58Fuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -61,6 +61,8 @@ int BlockFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
BlockFuzzer fuzzer; BlockFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -65,6 +65,8 @@ int BulletproofFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
BulletproofFuzzer fuzzer; BulletproofFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -95,7 +95,9 @@ int ColdOutputsFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
ColdOutputsFuzzer fuzzer; ColdOutputsFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -97,6 +97,8 @@ int ColdTransactionFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
ColdTransactionFuzzer fuzzer; ColdTransactionFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -92,7 +92,9 @@ int HTTPClientFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
HTTPClientFuzzer fuzzer; HTTPClientFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -341,7 +341,9 @@ int LevinFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
LevinFuzzer fuzzer; LevinFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -70,7 +70,9 @@ int PortableStorageFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
PortableStorageFuzzer fuzzer; PortableStorageFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -70,7 +70,9 @@ int PortableStorageFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
PortableStorageFuzzer fuzzer; PortableStorageFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -68,7 +68,9 @@ int ParseURLFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
ParseURLFuzzer fuzzer; ParseURLFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -92,6 +92,8 @@ int SignatureFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
SignatureFuzzer fuzzer; SignatureFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -61,6 +61,8 @@ int TransactionFuzzer::run(const std::string &filename)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
TRY_ENTRY();
TransactionFuzzer fuzzer; TransactionFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer); return run_fuzzer(argc, argv, fuzzer);
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -629,6 +629,7 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
TRY_ENTRY();
tools::on_startup(); tools::on_startup();
epee::debug::get_set_enable_assert(true, false); epee::debug::get_set_enable_assert(true, false);
//set up logging options //set up logging options
@ -636,4 +637,5 @@ int main(int argc, char** argv)
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -216,6 +216,7 @@ namespace
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
TRY_ENTRY();
tools::on_startup(); tools::on_startup();
//set up logging options //set up logging options
mlog_configure(mlog_get_default_log_path("net_load_tests_srv.log"), true); mlog_configure(mlog_get_default_log_path("net_load_tests_srv.log"), true);
@ -234,4 +235,5 @@ int main(int argc, char** argv)
if (!tcp_server.run_server(thread_count, true)) if (!tcp_server.run_server(thread_count, true))
return 2; return 2;
return 0; return 0;
CATCH_ENTRY_L0("main", 1);
} }

View file

@ -61,6 +61,7 @@ set(unit_tests_sources
mul_div.cpp mul_div.cpp
multiexp.cpp multiexp.cpp
multisig.cpp multisig.cpp
notify.cpp
parse_amount.cpp parse_amount.cpp
random.cpp random.cpp
serialization.cpp serialization.cpp
@ -123,4 +124,8 @@ SET_PROPERTY(SOURCE memwipe.cpp PROPERTY COMPILE_FLAGS -Ofast)
add_test( add_test(
NAME unit_tests NAME unit_tests
COMMAND unit_tests --data-dir "${TEST_DATA_DIR}") COMMAND unit_tests --data-dir "${TEST_DATA_DIR} --binary-dir ${CMAKE_BINARY_DIR}")
add_executable(test_notifier test_notifier.cpp)
target_link_libraries(test_notifier ${EXTRA_LIBRARIES})
set_property(TARGET test_notifier PROPERTY FOLDER "tests")

View file

@ -84,3 +84,24 @@ TEST(aligned, contents_smaller)
aligned_free(ptr2); aligned_free(ptr2);
} }
TEST(aligned, alignment)
{
static const size_t good_alignments[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
for (size_t a = 0; a <= 8192; ++a)
{
bool good = false;
for (const auto t: good_alignments) if (a == t) good = true;
void *ptr = aligned_malloc(1, a);
if (good)
{
ASSERT_TRUE(ptr != NULL);
aligned_free(ptr);
}
else
{
ASSERT_TRUE(ptr == NULL);
}
}
ASSERT_TRUE(aligned_malloc(1, ~0) == NULL);
}

View file

@ -0,0 +1,57 @@
// Copyright (c) 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 "gtest/gtest.h"
#include <boost/filesystem.hpp>
#include "misc_language.h"
#include "string_tools.h"
#include "file_io_utils.h"
#include "common/notify.h"
TEST(notify, works)
{
char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX";
int fd = mkstemp(name_template);
ASSERT_TRUE(fd >= 0);
close(fd);
const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier " + name_template + " %s";
tools::Notify notify(spec.c_str());
notify.notify("1111111111111111111111111111111111111111111111111111111111111111");
epee::misc_utils::sleep_no_w(100);
std::string s;
ASSERT_TRUE(epee::file_io_utils::load_file_to_string(name_template, s));
ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111");
boost::filesystem::remove(name_template);
}

View file

@ -136,21 +136,21 @@ TEST(ringdb, different_genesis)
ASSERT_FALSE(ringdb.get_ring(KEY_2, KEY_IMAGE_1, outs2)); ASSERT_FALSE(ringdb.get_ring(KEY_2, KEY_IMAGE_1, outs2));
} }
TEST(blackball, not_found) TEST(spent_outputs, not_found)
{ {
RingDB ringdb; RingDB ringdb;
ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_TRUE(ringdb.blackball(OUTPUT_1));
ASSERT_FALSE(ringdb.blackballed(OUTPUT_2)); ASSERT_FALSE(ringdb.blackballed(OUTPUT_2));
} }
TEST(blackball, found) TEST(spent_outputs, found)
{ {
RingDB ringdb; RingDB ringdb;
ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_TRUE(ringdb.blackball(OUTPUT_1));
ASSERT_TRUE(ringdb.blackballed(OUTPUT_1)); ASSERT_TRUE(ringdb.blackballed(OUTPUT_1));
} }
TEST(blackball, vector) TEST(spent_outputs, vector)
{ {
RingDB ringdb; RingDB ringdb;
std::vector<std::pair<uint64_t, uint64_t>> outputs; std::vector<std::pair<uint64_t, uint64_t>> outputs;
@ -174,7 +174,7 @@ TEST(blackball, vector)
ASSERT_TRUE(ringdb.blackballed(std::make_pair(30, 5))); ASSERT_TRUE(ringdb.blackballed(std::make_pair(30, 5)));
} }
TEST(blackball, unblackball) TEST(spent_outputs, mark_as_unspent)
{ {
RingDB ringdb; RingDB ringdb;
ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_TRUE(ringdb.blackball(OUTPUT_1));
@ -182,7 +182,7 @@ TEST(blackball, unblackball)
ASSERT_FALSE(ringdb.blackballed(OUTPUT_1)); ASSERT_FALSE(ringdb.blackballed(OUTPUT_1));
} }
TEST(blackball, clear) TEST(spent_outputs, clear)
{ {
RingDB ringdb; RingDB ringdb;
ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_TRUE(ringdb.blackball(OUTPUT_1));

View file

@ -0,0 +1,54 @@
// Copyright (c) 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv)
{
if (argc < 3)
{
fprintf(stderr, "usage: %s <filename> <hash>\n", argv[0]);
return 1;
}
const char *filename = argv[1];
const char *hash = argv[2];
FILE *f = fopen(filename, "a+");
if (!f)
{
fprintf(stderr, "error opening file %s: %s\n", filename, strerror(errno));
return 1;
}
fprintf(f, "%s", hash);
fclose(f);
return 0;
}