diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 83ffc96..764cd2b 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -15,10 +15,10 @@ jobs: preset: [default] include: - toolchain: gcc - docker: 1.237.0 + docker: 1.242.0 compiler: gcc - toolchain: clang - docker: 1.237.0 + docker: 1.242.0 compiler: clang steps: - name: Checkout opentxs diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 1660092..fe8104f 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false env: - docker: '1.237.0' + docker: '1.242.0' steps: - name: Checkout opentxs uses: actions/checkout@v3 diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index b2f6968..e02bfe8 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false env: - docker: '1.237.0' + docker: '1.242.0' steps: - name: Checkout opentxs uses: actions/checkout@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f61806..11e2e78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ otcommon_print_build_details(METIER_SERVER_PEDANTIC_BUILD "") otcommon_find_system_libraries() find_package( opentxs - 1.237 + 1.242 CONFIG REQUIRED ) @@ -83,4 +83,12 @@ find_package( # ----------------------------------------------------------------------------- # Build source +add_executable(${PROJECT_NAME} "") +otcommon_configure_target_cxx(${PROJECT_NAME}) +otcommon_define_signed_overflow(${PROJECT_NAME}) +target_link_libraries( + ${PROJECT_NAME} PUBLIC opentxs::libopentxs Boost::program_options +) +install(TARGETS ${PROJECT_NAME}) + add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7193afa..47e54fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,4 +3,9 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +target_include_directories( + ${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" +) + add_subdirectory(metier_server) diff --git a/src/metier_server/App.cpp b/src/metier_server/App.cpp new file mode 100644 index 0000000..d96d7fb --- /dev/null +++ b/src/metier_server/App.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "metier_server/App.hpp" // IWYU pragma: associated + +#include "metier_server/EventLoop.hpp" + +namespace metier_server +{ +using namespace std::literals; + +App::App(int argc, char** argv, opentxs::alloc::Strategy& alloc) noexcept(false) + : alloc_(alloc) + , options_(argc, argv, alloc_) +{ +} + +auto App::print_help() noexcept(false) -> int +{ + std::cout << options_.options() << '\n' << options_.ot_.HelpText() << '\n'; + + return 0; +} + +auto App::Run() noexcept(false) -> int +{ + if (options_.show_help_) { + + return print_help(); + } else { + + return run_daemon(); + } +} + +auto App::run_daemon() noexcept(false) -> int +{ + opentxs::api::Context::PrepareSignalHandling(); + auto const& ot = opentxs::start(options_.ot_); + ot.HandleSignals(); + auto const& client = ot.StartClientSession(options_.ot_, 0); + + if (options_.start_sync_server_) { + constexpr auto prefix = "tcp://"; + constexpr auto internal = "0.0.0.0"; + constexpr auto sep = ":"; + auto const& port = options_.sync_port_; + auto const nextport{port + 1}; + auto const started = client.Network().OTDHT().StartListener( + opentxs::String{prefix, alloc_.work_} + .append(internal) + .append(sep) + .append(std::to_string(port)), + opentxs::String{prefix, alloc_.work_} + .append(options_.sync_server_public_ip_) + .append(sep) + .append(std::to_string(port)), + opentxs::String{prefix, alloc_.work_} + .append(internal) + .append(sep) + .append(std::to_string(nextport)), + opentxs::String{prefix, alloc_.work_} + .append(options_.sync_server_public_ip_) + .append(sep) + .append(std::to_string(nextport))); + + if (false == started) { + + throw std::runtime_error{"failed to start otdht listener"}; + } + } + + for (auto const& chain : options_.enabled_chains_) { + if (false == client.Network().Blockchain().Enable(chain).IsValid()) { + + throw std::runtime_error{"unable to enable "s.append(print(chain))}; + } + } + + auto const running = client.StartSessionEventLoop( + [](auto const& api, auto& alloc) { + return std::allocate_shared(alloc.result_, api, alloc); + }, + {}, + {}, + 1, + alloc_); + + if (false == running) { + + throw std::runtime_error{"failed to start event loop"}; + } + + opentxs::join(); + + return 0; +} +} // namespace metier_server diff --git a/src/metier_server/App.hpp b/src/metier_server/App.hpp new file mode 100644 index 0000000..150d11a --- /dev/null +++ b/src/metier_server/App.hpp @@ -0,0 +1,26 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "metier_server/Options.hpp" + +namespace metier_server +{ +class App +{ +public: + auto Run() noexcept(false) -> int; + + App(int argc, char** argv, opentxs::alloc::Strategy& alloc) noexcept(false); + +private: + opentxs::alloc::Strategy& alloc_; + Options const options_; + + auto print_help() noexcept(false) -> int; + auto run_daemon() noexcept(false) -> int; +}; +} // namespace metier_server diff --git a/src/metier_server/CMakeLists.txt b/src/metier_server/CMakeLists.txt index ee3bda2..1f3d890 100644 --- a/src/metier_server/CMakeLists.txt +++ b/src/metier_server/CMakeLists.txt @@ -3,14 +3,14 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -set(cxx-sources main.cpp) - -add_executable(${PROJECT_NAME} ${cxx-sources}) -otcommon_configure_target_cxx(${PROJECT_NAME}) -otcommon_define_signed_overflow(${PROJECT_NAME}) - -target_link_libraries( - ${PROJECT_NAME} PUBLIC opentxs::libopentxs Boost::program_options +target_sources( + ${PROJECT_NAME} + PRIVATE + "App.cpp" + "App.hpp" + "EventLoop.cpp" + "EventLoop.hpp" + "Options.cpp" + "Options.hpp" + "main.cpp" ) - -install(TARGETS ${PROJECT_NAME}) diff --git a/src/metier_server/EventLoop.cpp b/src/metier_server/EventLoop.cpp new file mode 100644 index 0000000..0ef1f89 --- /dev/null +++ b/src/metier_server/EventLoop.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "metier_server/EventLoop.hpp" // IWYU pragma: associated + +namespace metier_server +{ +EventLoop::EventLoop( + opentxs::api::Session const& api, + opentxs::alloc::Strategy& alloc, + allocator_type) noexcept(false) + : opentxs::api::session::EventLoop(alloc.result_) + , enabled_chains_(sort_enabled_chains(api, alloc)) + , stats_(api.Network().Blockchain().Stats(alloc), alloc.result_) + , chain_print_width_([&] { + if (enabled_chains_.empty()) { + + return 0; + } else { + auto const get_width = [](auto type) noexcept { + return static_cast(print(type).size()); + }; + auto const max = std::ranges::max(enabled_chains_, {}, get_width); + + return get_width(max); + } + }()) +{ +} + +auto EventLoop::Description() const noexcept -> std::string_view +{ + return "Metier-server"sv; +} + +auto EventLoop::get_deleter() noexcept -> delete_function +{ + return opentxs::pmr::Deleter::Factory(*this); +} + +auto EventLoop::print_status( + opentxs::util::eventloop::state::ProcessMessage& state) noexcept(false) + -> void +{ + auto out = std::stringstream{}; + + { + out << std::setw(chain_print_width_) << " "; + out << std::setw(status_print_width_) << "overall "; + out << std::setw(status_print_width_) << "peer "; + out << std::setw(status_print_width_) << "block "; + out << std::setw(status_print_width_) << "block"; + out << std::setw(status_print_width_) << "cfheader"; + out << std::setw(status_print_width_) << "cfilter"; + out << '\n'; + } + + { + out << std::setw(chain_print_width_) << " "; + out << std::setw(status_print_width_) << "progress"; + out << std::setw(status_print_width_) << "count"; + out << std::setw(status_print_width_) << "headers"; + out << std::setw(status_print_width_) << "chain"; + out << std::setw(status_print_width_) << "chain "; + out << std::setw(status_print_width_) << "chain "; + out << '\n'; + } + + auto const print_status = [this, &out](auto const chain) { + out << std::setw(chain_print_width_) << print(chain); + out << std::setw(status_print_width_ - 1) << std::fixed + << std::setprecision(2) << stats_.Progress(chain) << "%"; + out << std::setw(status_print_width_) << stats_.PeerCount(chain); + out << std::setw(status_print_width_) + << stats_.BlockHeaderTip(chain).height_; + out << std::setw(status_print_width_) << stats_.BlockTip(chain).height_; + out << std::setw(status_print_width_) + << stats_.CfheaderTip(chain).height_; + out << std::setw(status_print_width_) + << stats_.CfilterTip(chain).height_; + out << '\n'; + }; + std::ranges::for_each(enabled_chains_, print_status); + std::cout << out.str() << std::endl; + reset_status_timer(state); +} + +auto EventLoop::Run( + opentxs::api::Session const&, + opentxs::util::eventloop::state::PreInit&, + opentxs::util::eventloop::Socket&, + std::span, + std::span, + opentxs::alloc::Strategy&) noexcept(false) -> bool +{ + return true; +} + +auto EventLoop::Run( + opentxs::api::Session const&, + opentxs::util::eventloop::state::Init& state, + opentxs::alloc::Strategy&) noexcept(false) -> bool +{ + reset_status_timer(state); + + return true; +} + +auto EventLoop::Run( + opentxs::api::Session const&, + opentxs::util::eventloop::state::ProcessMessage& state, + opentxs::util::eventloop::SocketIndex, + std::optional type, + opentxs::util::eventloop::Message&&, + opentxs::alloc::Strategy&) noexcept(false) -> bool +{ + using enum opentxs::util::WorkType; + + switch (auto const t = type.value_or(value(Unknown)); t) { + case print_status_: { + print_status(state); + } break; + default: { + + throw std::runtime_error{ + "received message of unsupported type "s.append( + opentxs::print_work_type(t))}; + } + } + + return true; +} + +auto EventLoop::sort_enabled_chains( + opentxs::api::Session const& api, + opentxs::alloc::Strategy& alloc) noexcept(false) -> EnabledChains +{ + auto sorted = [&] { + auto out = opentxs::Map< + std::string_view, + opentxs::blockchain::Type, + opentxs::NaturalCaseCompare>{alloc.work_}; + constexpr auto to_name = [](auto const& chain) { + return std::make_pair(print(chain), chain); + }; + auto const enabled = + api.Network().Blockchain().EnabledChains(alloc.WorkOnly()); + std::ranges::transform(enabled, std::inserter(out, out.end()), to_name); + + return out; + }(); + auto out = EnabledChains{alloc.result_}; + std::ranges::copy(sorted | std::views::values, std::back_inserter(out)); + + return out; +} + +EventLoop::~EventLoop() = default; +} // namespace metier_server diff --git a/src/metier_server/EventLoop.hpp b/src/metier_server/EventLoop.hpp new file mode 100644 index 0000000..8e6344f --- /dev/null +++ b/src/metier_server/EventLoop.hpp @@ -0,0 +1,72 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +namespace metier_server +{ +using namespace std::literals; +using namespace opentxs::literals; + +class EventLoop final : public opentxs::api::session::EventLoop +{ +public: + [[nodiscard]] auto Description() const noexcept -> std::string_view final; + + [[nodiscard]] auto get_deleter() noexcept -> delete_function final; + [[nodiscard]] auto Run( + opentxs::api::Session const& api, + opentxs::util::eventloop::state::PreInit& state, + opentxs::util::eventloop::Socket& subscribe, + std::span internal, + std::span external, + opentxs::alloc::Strategy& alloc) noexcept(false) -> bool final; + [[nodiscard]] auto Run( + opentxs::api::Session const& api, + opentxs::util::eventloop::state::Init& state, + opentxs::alloc::Strategy& alloc) noexcept(false) -> bool final; + [[nodiscard]] auto Run( + opentxs::api::Session const& api, + opentxs::util::eventloop::state::ProcessMessage& state, + opentxs::util::eventloop::SocketIndex index, + std::optional type, + opentxs::util::eventloop::Message&& message, + opentxs::alloc::Strategy& alloc) noexcept(false) -> bool final; + + EventLoop( + opentxs::api::Session const& api, + opentxs::alloc::Strategy& alloc, + allocator_type) noexcept(false); + + ~EventLoop() final; + +private: + using EnabledChains = opentxs::Vector; + + static constexpr auto status_timer_index_ = 0_uz; + static constexpr auto print_interval_ = 6s; + static constexpr auto print_status_ = + opentxs::first_user_defined_work_type_ + 0U; + static constexpr auto status_print_width_ = 10; + + EnabledChains const enabled_chains_; + opentxs::api::network::blockchain::Stats const stats_; + int const chain_print_width_; + + static auto sort_enabled_chains( + opentxs::api::Session const& api, + opentxs::alloc::Strategy& alloc) noexcept(false) -> EnabledChains; + + auto print_status( + opentxs::util::eventloop::state::ProcessMessage& state) noexcept(false) + -> void; + template + auto reset_status_timer(State& state) noexcept(false) -> void + { + get_timer(state, status_timer_index_) + .Activate(state, print_interval_, print_status_); + } +}; +} // namespace metier_server diff --git a/src/metier_server/Options.cpp b/src/metier_server/Options.cpp new file mode 100644 index 0000000..7c3d9bf --- /dev/null +++ b/src/metier_server/Options.cpp @@ -0,0 +1,224 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "metier_server/Options.hpp" // IWYU pragma: associated + +namespace metier_server +{ +using namespace std::literals; +using namespace opentxs::literals; + +Options::Options( + int argc, + char** argv, + opentxs::alloc::Strategy& alloc) noexcept(false) + : ot_() + , disabled_chains_(alloc.result_) + , enabled_chains_(alloc.result_) + , show_help_() + , sync_port_() + , start_sync_server_() + , sync_server_public_ip_(alloc.result_) +{ + read_options(argc, argv); + process_arguments(argc, argv); +} + +auto Options::lower(opentxs::UnallocatedString& s) noexcept + -> opentxs::UnallocatedString& +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { + return std::tolower(c); + }); + + return s; +} + +auto Options::options() noexcept + -> boost::program_options::options_description const& +{ + static auto const output = [] { + auto out = boost::program_options::options_description{ + "Metier-server options"}; + out.add_options()(help_, "Display this message"); + out.add_options()( + home_, + boost::program_options::value() + ->default_value( + opentxs::api::Context::SuggestFolder("metier-server")), + "Path to data directory"); + out.add_options()( + sync_server_, + boost::program_options::value(), + "Starting TCP port to use for sync server. Two ports will be " + "allocated."); + out.add_options()( + sync_public_ip_, + boost::program_options::value(), + "IP address or domain name where clients can connect to reach the " + "sync server. Mandatory if --sync_server is specified."); + out.add_options()( + all_, + boost::program_options::value(), + "(no arguments) Enable all supported blockchains. Seed nodes may " + "still be set by " + "passing the option for the appropriate chain.\nThe optional " + "argument \"off\" may be passed to disable all previously-enabled " + "chains."); + + for (auto const& chain : opentxs::blockchain::supported_chains()) { + auto ticker = opentxs::blockchain::ticker_symbol(chain); + auto message = std::stringstream{}; + message << "Enable " << opentxs::blockchain::print(chain) + << " blockchain.\nOptionally specify ip address of seed " + "node or \"off\" to disable"; + out.add_options()( + lower(ticker).c_str(), + boost::program_options::value() + ->implicit_value(""), + message.str().c_str()); + } + return out; + }(); + + return output; +} + +auto Options::parse( + std::string_view input, + opentxs::blockchain::Type type) noexcept -> void +{ + if (off_ == input) { + disabled_chains_.emplace(type); + enabled_chains_.erase(type); + } else { + enabled_chains_.emplace(type); + disabled_chains_.erase(type); + + if (false == input.empty()) { + ot_.AddBlockchainNativePeer(type, input); + } + } +} + +auto Options::process_arguments(int argc, char** argv) noexcept(false) -> void +{ + static auto const librarySupported = + opentxs::blockchain::supported_chains(); + static auto const excludeFromAll = [&] { + using enum opentxs::blockchain::Type; + + return std::set{ + BitcoinSV, BitcoinSV_testnet3}; + }(); + static auto const allChains = [&] { + auto out = std::vector(); + out.reserve(librarySupported.size()); + std::set_difference( + librarySupported.begin(), + librarySupported.end(), + excludeFromAll.begin(), + excludeFromAll.end(), + std::back_inserter(out)); + + return out; + }(); + static auto const tickers = [] { + auto out = opentxs:: + Map{}; + + for (auto const& chain : opentxs::blockchain::supported_chains()) { + auto ticker = opentxs::blockchain::ticker_symbol(chain); + lower(ticker); + out.emplace(std::move(ticker), chain); + } + + return out; + }(); + ot_.SetHome(opentxs::api::Context::SuggestFolder("metier-server").c_str()); + ot_.SetBlockchainProfile(opentxs::blockchain::Profile::server); + ot_.ParseCommandLine(argc, argv); + + for (auto const& [name, value] : variables()) { + if (name == help_) { + show_help_ = true; + } else if (name == all_) { + for (auto const chain : allChains) { + try { + if (off_ == value.as()) { + enabled_chains_.erase(chain); + disabled_chains_.emplace(chain); + } else if (false == disabled_chains_.contains(chain)) { + enabled_chains_.emplace(chain); + } + } catch (...) { + continue; + } + } + } else if (name == home_) { + try { + ot_.SetHome(value.as().c_str()); + } catch (...) { + } + } else if (name == sync_server_) { + try { + sync_port_ = 0; + ot_.SetBlockchainProfile(opentxs::blockchain::Profile::server); + } catch (...) { + } + } else if (name == sync_public_ip_) { + try { + sync_server_public_ip_ = + value.as(); + } catch (...) { + } + } else { + try { + auto input{name}; + auto const chain = tickers.at(lower(input)); + parse(value.as(), chain); + } catch (...) { + continue; + } + } + } + + for (auto const chain : disabled_chains_) { ot_.DisableBlockchain(chain); } + + start_sync_server_ = + (0 < sync_port_) && + (std::numeric_limits::max() > sync_port_); + ot_.SetBlockchainSyncEnabled(start_sync_server_); + + if (start_sync_server_) { + if (sync_server_public_ip_.empty()) { + + throw std::invalid_argument{ + "mandatory argument --public_addr not specified"}; + } + + using enum opentxs::network::blockchain::Transport; + // TODO parse the address to see if it is ipv4 or ipv6 + ot_.AddOTDHTListener(ipv4, sync_server_public_ip_, ipv4, "0.0.0.0"); + } +} + +auto Options::read_options(int argc, char** argv) noexcept(false) -> void +{ + auto const parsed = boost::program_options::command_line_parser(argc, argv) + .options(options()) + .allow_unregistered() + .run(); + boost::program_options::store(parsed, variables()); + boost::program_options::notify(variables()); +} + +auto Options::variables() noexcept -> boost::program_options::variables_map& +{ + static auto output = boost::program_options::variables_map{}; + + return output; +} +} // namespace metier_server diff --git a/src/metier_server/Options.hpp b/src/metier_server/Options.hpp new file mode 100644 index 0000000..b8c38c5 --- /dev/null +++ b/src/metier_server/Options.hpp @@ -0,0 +1,53 @@ +// Copyright (c) 2019-2024 The Open-Transactions developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#include +#pragma GCC diagnostic pop +#include + +namespace metier_server +{ +using namespace std::literals; + +class Options +{ +public: + static auto options() noexcept + -> boost::program_options::options_description const&; + + using Enabled = opentxs::Set; + using Disabled = opentxs::Set; + + opentxs::api::Options ot_; + Enabled disabled_chains_; + Enabled enabled_chains_; + bool show_help_; + int sync_port_; + bool start_sync_server_; + opentxs::String sync_server_public_ip_; + + Options(int argc, char** argv, opentxs::alloc::Strategy& alloc) noexcept( + false); + +private: + static constexpr auto all_ = "all"; + static constexpr auto help_ = "help"; + static constexpr auto home_ = "data_dir"; + static constexpr auto sync_public_ip_ = "public_addr"; + static constexpr auto sync_server_ = "sync_server"; + static constexpr auto off_ = "off"sv; + + static auto lower(opentxs::UnallocatedString& str) noexcept + -> opentxs::UnallocatedString&; + static auto read_options(int argc, char** argv) noexcept(false) -> void; + static auto variables() noexcept -> boost::program_options::variables_map&; + + auto parse(std::string_view input, opentxs::blockchain::Type type) noexcept + -> void; + auto process_arguments(int argc, char** argv) noexcept(false) -> void; +}; +} // namespace metier_server diff --git a/src/metier_server/main.cpp b/src/metier_server/main.cpp index 5422544..e51b9d8 100644 --- a/src/metier_server/main.cpp +++ b/src/metier_server/main.cpp @@ -3,393 +3,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnull-dereference" -#include - -#pragma GCC diagnostic pop #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wheader-hygiene" -using namespace std::literals; -#pragma GCC diagnostic pop - -using Type = opentxs::blockchain::Type; -using Enabled = opentxs::Set; -using Disabled = opentxs::Set; - -constexpr auto all_{"all"}; -constexpr auto help_{"help"}; -constexpr auto home_{"data_dir"}; -constexpr auto sync_public_ip_{"public_addr"}; -constexpr auto sync_server_{"sync_server"}; -struct Options { - opentxs::api::Options ot_{}; - Enabled enabled_chains_{}; - bool show_help_{}; - int sync_port_{}; - bool start_sync_server_{}; - opentxs::UnallocatedString sync_server_public_ip_{}; -}; - -auto options() noexcept -> boost::program_options::options_description const&; -auto lower(opentxs::UnallocatedString& str) noexcept - -> opentxs::UnallocatedString&; -auto parse( - opentxs::UnallocatedString const& input, - Type const type, - opentxs::api::Options& args, - Enabled& enabled, - Disabled& disabled) noexcept -> void; -auto process_arguments(Options& opts, int argc, char** argv) noexcept -> void; -auto read_options(int argc, char** argv) noexcept -> bool; -auto variables() noexcept -> boost::program_options::variables_map&; +#include "metier_server/App.hpp" auto main(int argc, char* argv[]) -> int { std::set_terminate(&opentxs::terminate_handler); try { - auto opts = Options{}; - - if (false == read_options(argc, argv)) { return 1; } - - process_arguments(opts, argc, argv); - - if (opts.show_help_) { - std::cout << ::options() << '\n' << opts.ot_.HelpText() << '\n'; - - return 0; - } - - if (opts.start_sync_server_) { - if (opts.sync_server_public_ip_.empty()) { - std::cout << "Mandatory argument --public_addr not specified\n"; - - return 1; - } - - using enum opentxs::network::blockchain::Transport; - // TODO parse the address to see if it is ipv4 or ipv6 - opts.ot_.AddOTDHTListener( - ipv4, opts.sync_server_public_ip_, ipv4, "0.0.0.0"); - } - - opentxs::api::Context::PrepareSignalHandling(); - auto const& ot = opentxs::start(opts.ot_); - ot.HandleSignals(); - auto const& client = ot.StartClientSession(opts.ot_, 0); - auto const enabled = [&] { - auto out = opentxs::Map< - std::string_view, - opentxs::blockchain::Type, - opentxs::NaturalCaseCompare>{}; - - for (auto const& chain : opts.enabled_chains_) { - if (client.Network().Blockchain().Enable(chain)) { - out.try_emplace(opentxs::blockchain::print(chain), chain); - } else { - - throw std::runtime_error{ - "unable to enable "s.append(print(chain))}; - } - } - - return out; - }(); - auto const sorted = [&] { - auto out = opentxs::Vector{}; - out.reserve(enabled.size()); - std::ranges::copy( - enabled | std::views::values, std::back_inserter(out)); + auto alloc = opentxs::alloc::Strategy{}; - return out; - }(); - - if (opts.start_sync_server_) { - constexpr auto prefix = "tcp://"; - constexpr auto internal = "0.0.0.0"; - constexpr auto sep = ":"; - auto const& port = opts.sync_port_; - auto const nextport{port + 1}; - auto const started = client.Network().OTDHT().StartListener( - opentxs::UnallocatedString{prefix} + internal + sep + - std::to_string(port), - opentxs::UnallocatedString{prefix} + - opts.sync_server_public_ip_ + sep + std::to_string(port), - opentxs::UnallocatedString{prefix} + internal + sep + - std::to_string(nextport), - opentxs::UnallocatedString{prefix} + - opts.sync_server_public_ip_ + sep + - std::to_string(nextport)); - - if (false == started) { - - throw std::runtime_error{"failed to start otdht listener"}; - } - } - - client.Schedule( - 6s, - [chains = sorted, - stats = client.Network().Blockchain().Stats()]() -> void { - static auto const widthChain = [] { - auto out = std::size_t{0}; - - for (auto const chain : - opentxs::blockchain::defined_chains()) { - out = std::max( - out, opentxs::blockchain::print(chain).size()); - } - - return static_cast(out + 2); - }(); - static constexpr auto width{10}; - auto out = std::stringstream{}; - - { - out << std::setw(widthChain) << " "; - out << std::setw(width) << "overall "; - out << std::setw(width) << "peer "; - out << std::setw(width) << "block "; - out << std::setw(width) << "block"; - out << std::setw(width) << "cfheader"; - out << std::setw(width) << "cfilter"; - out << '\n'; - } - - { - out << std::setw(widthChain) << " "; - out << std::setw(width) << "progress"; - out << std::setw(width) << "count"; - out << std::setw(width) << "headers"; - out << std::setw(width) << "chain"; - out << std::setw(width) << "chain "; - out << std::setw(width) << "chain "; - out << '\n'; - } - - for (auto const& chain : chains) { - out << std::setw(widthChain) << print(chain); - out << std::setw(width - 1) << std::fixed - << std::setprecision(2) << stats.Progress(chain) << "%"; - out << std::setw(width) << stats.PeerCount(chain); - out << std::setw(width) - << stats.BlockHeaderTip(chain).height_; - out << std::setw(width) << stats.BlockTip(chain).height_; - out << std::setw(width) << stats.CfheaderTip(chain).height_; - out << std::setw(width) << stats.CfilterTip(chain).height_; - out << '\n'; - } - - std::cout << out.str() << std::endl; - }); - - opentxs::join(); + return metier_server::App{argc, argv, alloc}.Run(); } catch (std::exception const& e) { opentxs::LogError()(e.what()).Flush(); - } - - return 0; -} - -auto lower(opentxs::UnallocatedString& s) noexcept - -> opentxs::UnallocatedString& -{ - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { - return std::tolower(c); - }); - return s; -} - -auto options() noexcept -> boost::program_options::options_description const& -{ - static auto const output = [] { - auto out = boost::program_options::options_description{ - "Metier-server options"}; - out.add_options()(help_, "Display this message"); - out.add_options()( - home_, - boost::program_options::value() - ->default_value( - opentxs::api::Context::SuggestFolder("metier-server")), - "Path to data directory"); - out.add_options()( - sync_server_, - boost::program_options::value(), - "Starting TCP port to use for sync server. Two ports will be " - "allocated."); - out.add_options()( - sync_public_ip_, - boost::program_options::value(), - "IP address or domain name where clients can connect to reach the " - "sync server. Mandatory if --sync_server is specified."); - out.add_options()( - all_, - "Enable all supported blockchains. Seed nodes may still be set by " - "passing the option for the appropriate chain."); - - for (auto const& chain : opentxs::blockchain::supported_chains()) { - auto ticker = opentxs::blockchain::ticker_symbol(chain); - auto message = std::stringstream{}; - message << "Enable " << opentxs::blockchain::print(chain) - << " blockchain.\nOptionally specify ip address of seed " - "node or \"off\" to disable"; - out.add_options()( - lower(ticker).c_str(), - boost::program_options::value() - ->implicit_value(""), - message.str().c_str()); - } - return out; - }(); - - return output; -} - -auto parse( - opentxs::UnallocatedString const& input, - Type const type, - opentxs::api::Options& args, - Enabled& enabled, - Disabled& disabled) noexcept -> void -{ - constexpr auto off{"off"}; - - if (input == off) { - disabled.emplace(type); - enabled.erase(type); - } else if (0u == disabled.count(type)) { - enabled.emplace(type); - args.AddBlockchainNativePeer(type, input); - } -} - -auto process_arguments(Options& opts, int argc, char** argv) noexcept -> void -{ - static auto const librarySupported = - opentxs::blockchain::supported_chains(); - static auto const excludeFromAll = [&] { - using enum opentxs::blockchain::Type; - - return std::set{ - BitcoinSV, BitcoinSV_testnet3}; - }(); - static auto const allChains = [&] { - auto out = std::vector(); - out.reserve(librarySupported.size()); - std::set_difference( - librarySupported.begin(), - librarySupported.end(), - excludeFromAll.begin(), - excludeFromAll.end(), - std::back_inserter(out)); - - return out; - }(); - auto map = opentxs::Map{}; - - for (auto const& chain : opentxs::blockchain::supported_chains()) { - auto ticker = opentxs::blockchain::ticker_symbol(chain); - lower(ticker); - map.emplace(std::move(ticker), chain); + return 1; } - - auto seed = opentxs::UnallocatedString{}; - auto& otargs = opts.ot_; - otargs.SetHome( - opentxs::api::Context::SuggestFolder("metier-server").c_str()); - otargs.SetBlockchainProfile(opentxs::blockchain::Profile::server); - otargs.ParseCommandLine(argc, argv); - auto& enabled = opts.enabled_chains_; - auto& syncPort = opts.sync_port_; - auto& publicIP = opts.sync_server_public_ip_; - auto disabled = opentxs::Set{}; - - for (auto const& [name, value] : variables()) { - if (name == help_) { - opts.show_help_ = true; - } else if (name == all_) { - for (auto const chain : allChains) { - if (0u == disabled.count(chain)) { - opts.enabled_chains_.emplace(chain); - } - } - } else if (name == home_) { - try { - otargs.SetHome(value.as().c_str()); - } catch (...) { - } - } else if (name == sync_server_) { - try { - syncPort = value.as(); - otargs.SetBlockchainProfile( - opentxs::blockchain::Profile::server); - } catch (...) { - } - } else if (name == sync_public_ip_) { - try { - publicIP = value.as(); - } catch (...) { - } - } else { - try { - auto input{name}; - auto const chain = map.at(lower(input)); - parse( - value.as(), - chain, - otargs, - enabled, - disabled); - } catch (...) { - continue; - } - } - } - - for (auto const chain : disabled) { otargs.DisableBlockchain(chain); } - - opts.start_sync_server_ = - (0 < syncPort) && - (std::numeric_limits::max() > syncPort); - otargs.SetBlockchainSyncEnabled(opts.start_sync_server_); -} - -auto read_options(int argc, char** argv) noexcept -> bool -{ - try { - auto const parsed = - boost::program_options::command_line_parser(argc, argv) - .options(options()) - .allow_unregistered() - .run(); - boost::program_options::store(parsed, variables()); - boost::program_options::notify(variables()); - - return true; - } catch (boost::program_options::error& e) { - std::cerr << "ERROR: " << e.what() << "\n\n" << options() << std::endl; - - return false; - } -} - -auto variables() noexcept -> boost::program_options::variables_map& -{ - static auto output = boost::program_options::variables_map{}; - - return output; }