mirror of
https://codeberg.org/wownero/lwcli-wow.git
synced 2026-10-17 05:51:26 +02:00
Add wallet inactivity timeout, and made wallet2 backend compile-time optional
This commit is contained in:
+4
-1
@@ -30,7 +30,10 @@ include_directories(.)
|
||||
add_subdirectory(decorate)
|
||||
add_subdirectory(views)
|
||||
|
||||
add_executable(lwcli main.cpp)
|
||||
add_executable(lwcli events.cpp main.cpp)
|
||||
target_include_directories(lwcli PRIVATE ".")
|
||||
target_link_libraries(lwcli PRIVATE lwsf-api component lwcli-views)
|
||||
|
||||
if (DEFINED ENABLE_WALLET2)
|
||||
target_compile_definitions(lwcli PRIVATE LWCLI_WALLET2_ENABLED)
|
||||
endif ()
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2025, Cifro Codes LLC
|
||||
//
|
||||
// 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 "events.h"
|
||||
|
||||
namespace lwcli { namespace event
|
||||
{
|
||||
/* Keep under 15 characters so that libstdc++ and libc++ can use small
|
||||
string optmization. */
|
||||
const ftxui::Event lock_wallet = ftxui::Event::Special("lwcli.lockw");
|
||||
}}
|
||||
+6
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
// Copyright (c) 2025, Cifro Codes LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
@@ -27,6 +26,9 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ftxui/component/event.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace lwcli { namespace event
|
||||
@@ -41,4 +43,7 @@ namespace lwcli { namespace event
|
||||
virtual ~close() noexcept override = default;
|
||||
virtual const char* what() const noexcept override final { return "close window"; }
|
||||
};
|
||||
|
||||
extern const ftxui::Event lock_wallet;
|
||||
|
||||
}} // lwcli // event
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace lwcli { namespace config
|
||||
|
||||
constexpr const std::string_view default_language{"English"};
|
||||
|
||||
//! Timeout interval for inactivity with open wallet
|
||||
constexpr const std::chrono::minutes wallet_timeout{2};
|
||||
namespace server
|
||||
{
|
||||
constexpr const std::string_view default_url{"http://127.0.0.1:8080"};
|
||||
|
||||
+101
-6
@@ -26,17 +26,24 @@
|
||||
// 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 <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ftxui/component/component.hpp>
|
||||
#include <ftxui/component/screen_interactive.hpp>
|
||||
#include <limits>
|
||||
#include <lws_frontend.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "events.h"
|
||||
#include "lwcli_config.h"
|
||||
#include "util.h"
|
||||
#include "views/manager.h"
|
||||
|
||||
namespace
|
||||
@@ -45,9 +52,10 @@ namespace
|
||||
struct program
|
||||
{
|
||||
std::string file;
|
||||
std::chrono::seconds wallet_timeout = lwcli::config::wallet_timeout;
|
||||
rpc backend = rpc::lws;
|
||||
// network is in static memory
|
||||
bool failed;
|
||||
bool failed = false;
|
||||
};
|
||||
|
||||
typedef const char**(*argument_handler)(program&, const char*[]);
|
||||
@@ -125,13 +133,34 @@ namespace
|
||||
|
||||
return ++argv;
|
||||
}
|
||||
const char** handle_timeout(program& prog, const char* argv[])
|
||||
{
|
||||
if (!argv || !argv[0])
|
||||
{
|
||||
fprintf(stderr, "Missing argument for --timeout\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto value = lwcli::from_string(argv[0]);
|
||||
if (!value || std::numeric_limits<std::chrono::seconds::rep>::max() < *value)
|
||||
{
|
||||
fprintf(stderr, "Invalid values for --timeout\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prog.wallet_timeout = std::chrono::seconds{*value};
|
||||
return ++argv;
|
||||
}
|
||||
|
||||
constexpr const argument process_args[] =
|
||||
{
|
||||
{nullptr, "help", "\t\t\tList help", 'h'},
|
||||
#ifdef LWCLI_WALLET2_ENABLED
|
||||
{handle_backend, "backend", "\tlws | monerod\t\tlws = default , selects rpc backend", 'b'},
|
||||
#endif
|
||||
{handle_file, "file", "\t[file path]\t\tDefaults to home directory. Auto-fills TUI value on launch", 'f'},
|
||||
{handle_network, "network", "\tmain | stage | test\tSelects wallet network type. main is default.", 'n'},
|
||||
{handle_timeout, "timeout", "\tseconds\tClose wallet after inactivity. Default 120", 't'}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
@@ -164,11 +193,72 @@ namespace
|
||||
++argv;
|
||||
return current->handler(prog, argv);
|
||||
}
|
||||
|
||||
struct screen_state
|
||||
{
|
||||
std::atomic<std::chrono::steady_clock::time_point::duration::rep> last_event;
|
||||
ftxui::ScreenInteractive screen;
|
||||
std::mutex sync;
|
||||
std::condition_variable notify;
|
||||
bool shutdown;
|
||||
|
||||
screen_state()
|
||||
: last_event(std::chrono::steady_clock::now().time_since_epoch().count()),
|
||||
screen(ftxui::ScreenInteractive::Fullscreen()),
|
||||
sync(),
|
||||
notify(),
|
||||
shutdown(false)
|
||||
{}
|
||||
};
|
||||
|
||||
struct watch_inactivity
|
||||
{
|
||||
screen_state& state;
|
||||
std::thread watcher;
|
||||
const std::chrono::seconds wallet_timeout;
|
||||
|
||||
watch_inactivity(screen_state& st, std::chrono::seconds wt)
|
||||
: state(st), watcher(), wallet_timeout(wt)
|
||||
{
|
||||
watcher = std::thread([this] () {
|
||||
std::unique_lock lock{state.sync};
|
||||
while (!state.shutdown)
|
||||
{
|
||||
const std::chrono::steady_clock::time_point last_event{
|
||||
std::chrono::steady_clock::time_point::duration{state.last_event.load()}
|
||||
};
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
auto time_diff = now - last_event;
|
||||
if (wallet_timeout <= time_diff)
|
||||
{
|
||||
state.last_event = now.time_since_epoch().count();
|
||||
state.screen.PostEvent(lwcli::event::lock_wallet);
|
||||
time_diff = std::chrono::seconds{0};
|
||||
}
|
||||
|
||||
state.notify.wait_for(lock, (wallet_timeout - time_diff), [this] () {
|
||||
return state.shutdown;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~watch_inactivity() noexcept // abort if double exception
|
||||
{
|
||||
std::unique_lock lock{state.sync};
|
||||
state.shutdown = true;
|
||||
state.notify.notify_one();
|
||||
lock.unlock();
|
||||
if (watcher.joinable())
|
||||
watcher.join();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main(int, const char* argv[])
|
||||
{
|
||||
auto screen = ftxui::ScreenInteractive::Fullscreen();
|
||||
screen_state state{};
|
||||
try
|
||||
{
|
||||
if (!argv)
|
||||
@@ -190,31 +280,36 @@ int main(int, const char* argv[])
|
||||
case rpc::lws:
|
||||
wm.reset(lwsf::WalletManagerFactory::getWalletManager());
|
||||
break;
|
||||
#ifdef LWCLI_WALLET2_ENABLED
|
||||
case rpc::monerod:
|
||||
wm.reset(Monero::WalletManagerFactory::getWalletManager());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
auto window = ftxui::CatchEvent(lwcli::view::manager(std::move(wm), std::move(prog.file)), [&] (ftxui::Event event)
|
||||
{
|
||||
state.last_event = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
if (event == ftxui::Event::CtrlC)
|
||||
{
|
||||
screen.ExitLoopClosure()();
|
||||
state.screen.ExitLoopClosure()();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
screen.Loop(window);
|
||||
|
||||
const watch_inactivity watch{state, prog.wallet_timeout};
|
||||
state.screen.Loop(window);
|
||||
}
|
||||
catch (const lwcli::event::close&)
|
||||
{}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
screen.Clear();
|
||||
state.screen.Clear();
|
||||
fprintf(stderr, "Fatal Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
screen.Clear();
|
||||
state.screen.Clear();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2025, Cifro Codes LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace lwcli
|
||||
{
|
||||
inline std::optional<std::uint64_t> from_string(const std::string_view str) noexcept
|
||||
{
|
||||
std::uint64_t height = 0;
|
||||
const auto end = str.data() + str.size();
|
||||
auto [ptr, ec] = std::from_chars(str.data(), end, height);
|
||||
if (bool(ec) || ptr != end)
|
||||
return std::nullopt;
|
||||
return height;
|
||||
}
|
||||
}
|
||||
+14
-13
@@ -40,6 +40,7 @@
|
||||
#include "events.h"
|
||||
#include "lwcli_config.h"
|
||||
#include "translate.h"
|
||||
#include "util.h"
|
||||
#include "views/history.h"
|
||||
#include "views/keys.h"
|
||||
|
||||
@@ -142,16 +143,6 @@ namespace lwcli { namespace view
|
||||
{}
|
||||
};
|
||||
|
||||
std::optional<std::uint64_t> from_string(const std::string_view str) noexcept
|
||||
{
|
||||
std::uint64_t height = 0;
|
||||
const auto end = str.data() + str.size();
|
||||
auto [ptr, ec] = std::from_chars(str.data(), end, height);
|
||||
if (bool(ec) || ptr != end)
|
||||
return std::nullopt;
|
||||
return height;
|
||||
}
|
||||
|
||||
bool init_wallet(Monero::Wallet& wal, std::string* error)
|
||||
{
|
||||
auto refresh = from_string(wal.getCacheAttribute(std::string{config::server::refresh_interval}));
|
||||
@@ -545,7 +536,9 @@ namespace lwcli { namespace view
|
||||
|
||||
try
|
||||
{
|
||||
if (state_.overlay)
|
||||
if (event == event::lock_wallet)
|
||||
state_.error = _("Wallet Locked Due to Inactivity");
|
||||
else if (state_.overlay)
|
||||
return state_.overlay->OnEvent(std::move(event));
|
||||
else if (event == ftxui::Event::CtrlQ)
|
||||
throw event::close{};
|
||||
@@ -619,8 +612,16 @@ namespace lwcli { namespace view
|
||||
try
|
||||
{
|
||||
if (wallet_)
|
||||
return wallet_->OnEvent(std::move(event));
|
||||
else if (start_->OnEvent(std::move(event)) && data_)
|
||||
{
|
||||
if (event == event::lock_wallet)
|
||||
{
|
||||
wallet_.reset();
|
||||
start_->OnEvent(std::move(event));
|
||||
}
|
||||
else
|
||||
return wallet_->OnEvent(std::move(event));
|
||||
}
|
||||
else if (event != event::lock_wallet && start_->OnEvent(std::move(event)) && data_)
|
||||
wallet_ = view::wallet(std::move(data_));
|
||||
data_.reset();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user