diff --git a/client/docker-finance.d/client/Dockerfiles/finance/Dockerfile.archlinux.in b/client/docker-finance.d/client/Dockerfiles/finance/Dockerfile.archlinux.in index 504c44e..c247a95 100644 --- a/client/docker-finance.d/client/Dockerfiles/finance/Dockerfile.archlinux.in +++ b/client/docker-finance.d/client/Dockerfiles/finance/Dockerfile.archlinux.in @@ -54,6 +54,18 @@ RUN pacman -Syu --noconfirm #WORKDIR /home/@DOCKER_FINANCE_USER@ #RUN pipx install xlsx2csv +## +## Plugins (dependencies) +## + +## libbitcoinkernel (bitcoin) +#RUN pacman -Syu \ +# boost \ +# capnproto \ +# cmake \ +# db \ +# --noconfirm --disable-download-timeout + ## Add yours below, as needed diff --git a/client/plugins/docker/bitcoin.bash b/client/plugins/docker/bitcoin.bash new file mode 100755 index 0000000..dfcf2a8 --- /dev/null +++ b/client/plugins/docker/bitcoin.bash @@ -0,0 +1,341 @@ +#!/usr/bin/env bash + +# docker-finance | modern accounting for the power-user +# +# Copyright (C) 2025 Aaron Fiore (Founder, Evergreen Crypto LLC) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# NOTES: +# +# This plugin provides a rudimentary clone/pull/build process that +# adheres to the expectations from the container-side bitcoin plugin. +# +# The only requirement for this plugin is that build dependencies +# are installed on your existing/running image (which is assumed to +# have also been built with the `root` module enabled): +# +# 1. Uncomment plugin's image build requirements: +# +# $ dfi edit type=build +# +# 2. Build/re-build your image with `root` module: +# +# $ dfi build type=default +# +# If you do bitcoin development on your client (host) and prefer to +# use your own build process, you can skip the above requirements and, +# instead, ensure the following: +# +# 1. Your bitcoin repository is dropped into your client-side shared +# directory: +# +# $DOCKER_FINANCE_CLIENT_SHARED +# +# The expected path by the container-side plugin will be in your +# container-side shared directory: +# +# "${DOCKER_FINANCE_CONTAINER_SHARED}"/bitcoin +# +# To see or edit your shared client/container path prior to +# running this (or the container's) plugin, run the following: +# +# $ dfi edit type=env +# +# 2. Bitcoin's libraries are built as a shared (when possible) and +# should continue to remain within the repo's build directory, +# prior to running the container-side `root` bitcoin plugin. +# + +# +# "Libraries" +# + +[ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && exit 1 +source "${DOCKER_FINANCE_CLIENT_REPO}/client/src/docker/lib/lib_docker.bash" || exit 1 + +[[ -z "$global_platform" || -z "$global_arg_delim_1" || -z "$global_user" || -z "$global_tag" ]] && lib_utils::die_fatal +instance="${global_platform}${global_arg_delim_1}${global_user}:${global_tag}" + +# Initialize "constructor" +# NOTE: "constructor" only needed if calling library directly +lib_docker::docker "$instance" || lib_utils::die_fatal + +# +# Implementation +# + +# WARNING: *MUST* be synced with image (assuming Arch Linux finance image) +declare -r plugin_bitcoin_deps=("boost" "capnproto" "cmake" "db") + +# WARNING: *MUST* be synced with container's plugin implementation +[ -z "$DOCKER_FINANCE_CONTAINER_SHARED" ] && lib_utils::die_fatal +declare -r plugin_bitcoin_path="${DOCKER_FINANCE_CONTAINER_SHARED}/bitcoin" + +function bitcoin::__parse_args() +{ + [ -z "$global_usage" ] && lib_utils::die_fatal + [ -z "$global_arg_delim_1" ] && lib_utils::die_fatal + [ -z "$global_arg_delim_2" ] && lib_utils::die_fatal + + local -r _default_arg_remote="https://github.com/bitcoin/bitcoin" + local -r _default_arg_branch="master" + local -r _default_arg_cmake="-DBUILD_BITCOIN_BIN=OFF -DBUILD_CLI=OFF -DBUILD_DAEMON=OFF -DBUILD_TESTS=OFF -DBUILD_UTIL=OFF -DBUILD_WALLET_TOOL=OFF -DINSTALL_MAN=OFF -DSECP256K1_BUILD_TESTS=OFF -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON" + + # Re-seat global usage + local _global_usage + _global_usage="$global_usage plugins repo${global_arg_delim_1}$(basename $0)" + declare -r _global_usage + + local -r _usage=" +\e[32mDescription:\e[0m + + Build bitcoin libraries for \`root\` container-side plugin. + +\e[32mUsage:\e[0m + + $ $_global_usage [remote${global_arg_delim_2}] [branch${global_arg_delim_2}] [cmake${global_arg_delim_2}] + +\e[32mArguments:\e[0m + + Git remote to get; default: + + remote${global_arg_delim_2}\"$_default_arg_remote\" + + Git branch to build from; default: + + branch${global_arg_delim_2}\"$_default_arg_branch\" + + CMake build options; default: + + cmake${global_arg_delim_2}\"$_default_arg_cmake\" + +\e[32mExamples:\e[0m + + \e[37;2m# Get from default\e[0m + $ $_global_usage get + + \e[37;2m# Get from given remote 'https://my.custom.remote/bitcoin'\e[0m + $ $_global_usage get remote${global_arg_delim_2}\"https://my.custom.remote/bitcoin\" + + \e[37;2m# Build default branch\e[0m + $ $_global_usage build + + \e[37;2m# Checkout and build given branch\e[0m + $ $_global_usage build branch=\"my_dev\" + + \e[37;2m# Checkout and build given commit\e[0m + $ $_global_usage build branch=\"b30262dcaa28c40a0a5072847b7194b3db203160\" + + \e[37;2m# Build given branch with custom CMake options\e[0m + $ $_global_usage build branch=\"my_dev\" cmake${global_arg_delim_2}\"-DBUILD_BITCOIN_BIN=ON -DBUILD_CLI=ON -DBUILD_DAEMON=ON -DBUILD_TESTS=ON -DBUILD_UTIL=ON -DBUILD_WALLET_TOOL=ON -DINSTALL_MAN=ON -DSECP256K1_BUILD_TESTS=ON -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=ON -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON\" + + \e[37;2m# Clean entire build directory\e[0m + $ $_global_usage clean +" + + [ $# -eq 0 ] && lib_utils::die_usage "$_usage" + + [[ ! "$1" =~ ^get$|^build$|^clean$ ]] \ + && lib_utils::die_usage "$_usage" \ + || declare -gr plugin_arg_cmd="$1" + + for _arg in "${@:2}"; do + [[ ! "$_arg" =~ ^remote${global_arg_delim_2} ]] \ + && [[ ! "$_arg" =~ ^branch${global_arg_delim_2} ]] \ + && [[ ! "$_arg" =~ ^cmake${global_arg_delim_2} ]] \ + && lib_utils::die_usage "$_usage" + done + + for _arg in "${@:2}"; do + local _key="${_arg%${global_arg_delim_2}*}" + local _len="$((${#_key} + 1))" + + if [[ "$_key" =~ ^remote$ ]]; then + local _arg_remote="${_arg:${_len}}" + [ -z "$_arg_remote" ] && lib_utils::die_usage "$_usage" + fi + + if [[ "$_key" =~ ^branch$ ]]; then + local _arg_branch="${_arg:${_len}}" + [ -z "$_arg_branch" ] && lib_utils::die_usage "$_usage" + fi + + if [[ "$_key" =~ ^cmake$ ]]; then + local _arg_cmake="${_arg:${_len}}" + [ -z "$_arg_cmake" ] && lib_utils::die_usage "$_usage" + fi + done + + # Arg: get + if [[ "$plugin_arg_cmd" =~ ^get$ ]]; then + if [[ ! -z "$_arg_branch" || ! -z "$_arg_cmake" ]]; then + lib_utils::die_usage "$_usage" + fi + + # Arg: remote + if [ -z "$_arg_remote" ]; then + _arg_remote="$_default_arg_remote" + fi + declare -gr plugin_arg_remote="$_arg_remote" + lib_utils::print_info "Using remote: ${_arg_remote}" + fi + + # Arg: build + if [[ "$plugin_arg_cmd" =~ ^build$ ]]; then + if [[ ! -z "$_arg_remote" ]]; then + lib_utils::die_usage "$_usage" + fi + + # Arg: branch + if [ -z "$_arg_branch" ]; then + _arg_branch="$_default_arg_branch" + fi + declare -gr plugin_arg_branch="$_arg_branch" + lib_utils::print_info "Using branch: ${plugin_arg_branch}" + + # Arg: cmake + if [ -z "$_arg_cmake" ]; then + _arg_cmake="$_default_arg_cmake" + fi + declare -gr plugin_arg_cmake="$_arg_cmake" + lib_utils::print_info "Using CMake options: ${plugin_arg_cmake}" + fi + + # Arg: clean + if [[ "$plugin_arg_cmd" =~ ^clean$ ]]; then + if [[ ! -z "$_arg_remote" || ! -z "$_arg_branch" || ! -z "$_arg_cmake" ]]; then + lib_utils::die_usage "$_usage" + fi + fi +} + +function bitcoin::__getter() +{ + [ -z "$plugin_arg_remote" ] && lib_utils::die_fatal + [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal + + lib_docker::exec "if [ ! -d $plugin_bitcoin_path ]; then git clone $plugin_arg_remote $plugin_bitcoin_path ; fi" \ + || lib_utils::die_fatal "Could not clone '${plugin_arg_remote}' to '${plugin_bitcoin_path}'" + + lib_docker::exec "pushd $plugin_bitcoin_path 1>/dev/null && git pull $plugin_arg_remote && popd 1>/dev/null" \ + || lib_utils::die_fatal "Could not pull '${plugin_arg_remote}' to '${plugin_bitcoin_path}'" +} + +function bitcoin::__builder() +{ + # + # Ensure dependencies + # + + local -r _no_deps="Build dependencies not found! + + Did you uncomment them with: + +\t\$ dfi $instance edit type=build + + and then re-build your image with: + +\t\$ dfi $instance build type=" + + # NOTE: *MUST* be synced with end-user Dockerfile (`edit type=build`) + # TODO: not ideal, would prefer runtime dependency pulling but that has drawbacks as well + [ -z "${plugin_bitcoin_deps[*]}" ] && lib_utils::die_fatal + lib_docker::exec "pacman -Q ${plugin_bitcoin_deps[*]}" | grep "^error: package" \ + && lib_utils::die_fatal "$_no_deps" + + # + # Execute build + # + + [ -z "$plugin_arg_cmake" ] && lib_utils::die_fatal + [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal + + lib_docker::exec "pushd $plugin_bitcoin_path 1>/dev/null && git checkout $plugin_arg_branch && popd 1>/dev/null" \ + || lib_utils::die_fatal "Could not checkout '${plugin_arg_branch}'" + + lib_docker::exec "cmake $plugin_arg_cmake -B ${plugin_bitcoin_path}/build $plugin_bitcoin_path" \ + || lib_utils::die_fatal "Could not prepare build with given options" + + lib_docker::exec "cmake --build ${plugin_bitcoin_path}/build -j$(nproc)" \ + || lib_utils::die_fatal "Could not build" + + lib_utils::print_info "$(lib_docker::exec "find ${plugin_bitcoin_path} -name libbitcoinkernel.so")" +} + +function bitcoin::__cleaner() +{ + [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal + local -r _build="${plugin_bitcoin_path}/build" + + lib_utils::print_info "Removing '${_build}'" + lib_docker::exec "rm -fr $_build" || lib_utils::die_fatal "Could not delete= '${_build}'" +} + +# +# Facade +# + +function bitcoin::get() +{ + lib_utils::print_info "Getting repository" + bitcoin::__getter +} + +function bitcoin::build() +{ + lib_utils::print_info "Building repository" + bitcoin::__builder +} + +function bitcoin::clean() +{ + lib_utils::print_info "Cleaning repository" + bitcoin::__cleaner +} + +function main() +{ + bitcoin::__parse_args "$@" + + # Ensure running instance + local -r _no_instance="Did you start a running instance? + + Run the following in a separate shell: + +\t$ dfi $instance up" + + lib_docker::exec "" || lib_utils::die_fatal "$_no_instance" + + [ -z "$plugin_arg_cmd" ] && lib_utils::die_fatal + case "$plugin_arg_cmd" in + get) + bitcoin::get + ;; + build) + bitcoin::build + ;; + clean) + bitcoin::clean + ;; + *) + lib_utils::die_fatal "Not implemented" + ;; + esac +} + +main "$@" + +# vim: sw=2 sts=2 si ai et diff --git a/container/plugins/root/bitcoin/bitcoin.cc b/container/plugins/root/bitcoin/bitcoin.cc new file mode 100644 index 0000000..ec98941 --- /dev/null +++ b/container/plugins/root/bitcoin/bitcoin.cc @@ -0,0 +1,104 @@ +// docker-finance | modern accounting for the power-user +// +// Copyright (C) 2025 Aaron Fiore (Founder, Evergreen Crypto LLC) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation either version 3 of the License or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not see . + +//! \file +//! \author Aaron Fiore (Founder, Evergreen Crypto LLC) +//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter +//! \since docker-finance 1.1.0 + +//! \namespace dfi::plugin +//! \brief docker-finance plugins +//! \warning All plugins (repo/custom) must exist within this namespace +//! and work within their own inner namespace +//! \since docker-finance 1.0.0 +namespace dfi::plugin +{ +//! \namespace dfi::plugin::bitcoin +//! \brief docker-finance bitcoin plugin namespace +//! \warning Bitcoin inherently pollutes the global namespace - be wary +//! \since docker-finance 1.1.0 +namespace bitcoin +{ +//! \brief Get container-side path to bitcoin repository +//! \return Absolute path of shared (bind-mounted) bitcoin repository +//! \todo To avoid future include conflicts, consider adjusting base path +//! \since docker-finance 1.1.0 +inline std::string get_repo_path() +{ + return ::dfi::common::get_env("DOCKER_FINANCE_CONTAINER_SHARED") + + "/bitcoin/"; +} + +//! \brief Initialize this bitcoin plugin +//! \details Bare-essential initializations, will require manual loading for any further functionality +//! \warning Initializations *MUST* occur here before any other bitcoin lib use +//! \ingroup cpp_plugin_impl +//! \since docker-finance 1.1.0 +void load(const std::string& arg = {}) +{ + namespace common = ::dfi::common; + + const std::string repo{get_repo_path()}; + + // Minimum requirements + common::add_include_dir(repo + "src"); + common::add_linked_lib(repo + "build/lib/libbitcoinkernel.so"); + // WARNING: assertions *MUST* be enabled before loading any bitcoin headers + common::line("#undef NDEBUG"); + common::load(repo + "src/kernel/bitcoinkernel_wrapper.h"); + + // `dfi`-specific requirements (API, macros, tests, etc.) + common::line("#define __DFI_PLUGIN_BITCOIN__"); + common::load(repo + "src/random.h"); + // ...add as needed + + if (!arg.empty()) + common::line(arg); +} + +//! \brief Deinitialize this bitcoin plugin +//! \details Bare-essential deinitializations, will require manual unloading for any other previously loaded functionality +//! \warning Unloading certain bitcoin headers may result in Cling segfault due to bitcoin library design +//! \ingroup cpp_plugin_impl +//! \since docker-finance 1.1.0 +//! \todo It appears that, due to bitcoin design, unloading will result in +//! Cling segfault "*** Break *** illegal instruction". With that said, it's +//! best to exit `root` if you want a clean workspace (instead of unloading this plugin). +void unload(const std::string& arg = {}) +{ + namespace common = ::dfi::common; + + if (!arg.empty()) + common::line(arg); + + const std::string repo{get_repo_path()}; + + // `dfi`-specific requirements (API, macros, tests, etc.) + common::line("#undef __DFI_PLUGIN_BITCOIN__"); + common::unload(repo + "src/random.h"); + // ...add as needed + + // Minimum requirements + common::unload(repo + "src/kernel/bitcoinkernel_wrapper.h"); + common::line("#define NDEBUG"); + // TODO(unassigned): remove_include_dir() + common::remove_linked_lib(repo + "build/lib/libbitcoinkernel.so"); +} +} // namespace bitcoin +} // namespace dfi::plugin + +// # vim: sw=2 sts=2 si ai et diff --git a/container/src/root/macro/common/crypto.hh b/container/src/root/macro/common/crypto.hh index 276ccfb..32f9bfb 100644 --- a/container/src/root/macro/common/crypto.hh +++ b/container/src/root/macro/common/crypto.hh @@ -105,6 +105,21 @@ using Random = ::dfi::crypto::libsodium::Random; auto g_Random = std::make_unique(); } // namespace crypto::libsodium +#ifdef __DFI_PLUGIN_BITCOIN__ +//! \namespace dfi::macro::common::crypto::bitcoin +//! \since docker-finance 1.1.0 +namespace crypto::bitcoin +{ +//! \brief ROOT's bitcoin Random class +//! \ingroup cpp_macro +using Random = ::dfi::crypto::bitcoin::Random; + +//! \brief ROOT's bitcoin Random instance +//! \ingroup cpp_macro +auto g_Random = std::make_unique(); +} // namespace crypto::bitcoin +#endif + } // namespace common } // namespace macro } // namespace dfi diff --git a/container/src/root/macro/crypto/random.C b/container/src/root/macro/crypto/random.C index 769b0d6..bd0da58 100644 --- a/container/src/root/macro/crypto/random.C +++ b/container/src/root/macro/crypto/random.C @@ -47,6 +47,9 @@ namespace common = ::dfi::macro::common; namespace libsodium = common::crypto::libsodium; namespace cryptopp = common::crypto::cryptopp; namespace botan = common::crypto::botan; +#ifdef __DFI_PLUGIN_BITCOIN__ +namespace bitcoin = common::crypto::bitcoin; +#endif //! \brief CSPRNG macro class Random final @@ -110,6 +113,24 @@ class Random final return rng; } +#ifdef __DFI_PLUGIN_BITCOIN__ + //! \brief bitcon RNG + //! \return t_rng Random map to print (label, num) + static t_rng bitcoin_generate() + { + t_rng rng; + + rng["int16_t (unsupported)"] = 0; + rng["int32_t (unsupported)"] = 0; + + rng["uint16_t (unsupported)"] = 0; + rng["uint32_t"] = bitcoin::g_Random->generate(); + rng["uint64_t"] = bitcoin::g_Random->generate(); + + return rng; + } +#endif + public: //! \brief Print t_rng of CSPRNG numbers in CSV format static void generate() @@ -122,6 +143,9 @@ class Random final }; std::cout << "\nimpl,type,num\n"; +#ifdef __DFI_PLUGIN_BITCOIN__ + print("bitcoin::Random", Random::bitcoin_generate()); +#endif print("botan::Random", Random::botan_generate()); print("cryptopp::Random", Random::cryptopp_generate()); print("libsodium::Random", Random::libsodium_generate()); diff --git a/container/src/root/macro/web/internal/crypto.C b/container/src/root/macro/web/internal/crypto.C index 1f7ef65..18ec291 100644 --- a/container/src/root/macro/web/internal/crypto.C +++ b/container/src/root/macro/web/internal/crypto.C @@ -351,6 +351,15 @@ class Random final const std::string timestamp{common::make_timestamp()}; +#ifdef __DFI_PLUGIN_BITCOIN__ + data.title = "Bitcoin_FastRandomContext_32_RNG_" + timestamp; + random(data, []() -> uint32_t { + return common::crypto::bitcoin::g_Random->generate(); + }); + + // TODO(afiore): uint64_t (requires canvas-related refactor) +#endif + data.title = "Botan_RNG_" + timestamp; random(data, []() -> uint32_t { return common::crypto::botan::g_Random->generate(); diff --git a/container/src/root/src/internal/impl/random.hh b/container/src/root/src/internal/impl/random.hh index bb158ab..877fb25 100644 --- a/container/src/root/src/internal/impl/random.hh +++ b/container/src/root/src/internal/impl/random.hh @@ -19,6 +19,7 @@ //! \author Aaron Fiore (Founder, Evergreen Crypto LLC) //! \note File intended to be loaded into ROOT.cern framework / Cling interpreter //! \since docker-finance 1.0.0 +//! \todo C++20 refactor #ifndef CONTAINER_SRC_ROOT_SRC_INTERNAL_IMPL_RANDOM_HH_ #define CONTAINER_SRC_ROOT_SRC_INTERNAL_IMPL_RANDOM_HH_ @@ -259,6 +260,69 @@ class Random : public common::RandomImpl }; } // namespace libsodium +#ifdef __DFI_PLUGIN_BITCOIN__ +//! \namespace dfi::crypto::impl::bitcoin +//! \since docker-finance 1.1.0 +namespace bitcoin +{ +//! \concept dfi::crypto::impl::bitcoin::RType +//! \brief Random number type +//! \details Requirements include that "interface" specialization +//! has not changed and that given type is supported by bitcoin. +//! \since docker-finance 1.1.0 +template +concept RType = + ::dfi::internal::type::is_real_integral::value + && (std::same_as || std::same_as); + +//! \brief Implements Random API with bitcoin +//! \ingroup cpp_API_impl +//! \since docker-finance 1.1.0 +//! \todo span/bytes +//! \todo reseed +//! \todo insecure RNG option +class Random : public common::RandomImpl +{ + public: + Random() = default; + ~Random() = default; + + Random(const Random&) = delete; + Random& operator=(const Random&) = delete; + + Random(Random&&) = delete; + Random& operator=(Random&&) = delete; + + private: + //! \brief Implements random number generator + template + t_random generate_impl() + { + if constexpr (std::same_as) + { + return m_ctx.rand32(); + } + else if constexpr (std::same_as) + { + return m_ctx.rand64(); + } + } + + public: + //! \brief Generate random number + template + t_random generate() + { + return this->generate_impl(); + } + + private: + //! \warning Bitcoin's RNG is not thread-safe (but dfi's CRTP provides external locks, by default) + ::FastRandomContext m_ctx; +}; +} // namespace bitcoin +#endif + } // namespace impl } // namespace crypto } // namespace dfi diff --git a/container/src/root/src/random.hh b/container/src/root/src/random.hh index 2eeb56a..ec20839 100644 --- a/container/src/root/src/random.hh +++ b/container/src/root/src/random.hh @@ -118,6 +118,20 @@ namespace libsodium using Random = ::dfi::crypto::common::Random; } // namespace libsodium +#ifdef __DFI_PLUGIN_BITCOIN__ +//! \namespace dfi::crypto::bitcoin +//! \brief Public-facing API namespace (bitcoin) +//! \since docker-finance 1.1.0 +namespace bitcoin +{ +//! \brief bitcoin Random API specialization ("interface" / implementation) +//! \note For public consumption +//! \ingroup cpp_API +//! \since docker-finance 1.1.0 +using Random = ::dfi::crypto::common::Random; +} // namespace bitcoin +#endif + } // namespace crypto } // namespace dfi diff --git a/container/src/root/test/benchmark/random.hh b/container/src/root/test/benchmark/random.hh index 9d836bb..05116ec 100644 --- a/container/src/root/test/benchmark/random.hh +++ b/container/src/root/test/benchmark/random.hh @@ -128,6 +128,30 @@ BENCHMARK_F(RandomLibsodium, generate)(::benchmark::State& state) // NOLINT random.generate(); } } + +// +// Bitcoin +// + +#ifdef __DFI_PLUGIN_BITCOIN__ +//! \brief Bitcoin Random fixture w/ real implementation +//! \since docker-finance 1.1.0 +struct RandomBitcoin : public ::benchmark::Fixture, + public tests::RandomBitcoin_Impl +{ + void SetUp(const ::benchmark::State& state) {} + void TearDown(const ::benchmark::State& state) {} +}; + +BENCHMARK_F(RandomBitcoin, generate)(::benchmark::State& state) // NOLINT +{ + for (auto st : state) + { + random.generate(); + } +} +#endif + } // namespace benchmarks } // namespace tests } // namespace dfi diff --git a/container/src/root/test/common/random.hh b/container/src/root/test/common/random.hh index 3b83312..ee7e397 100644 --- a/container/src/root/test/common/random.hh +++ b/container/src/root/test/common/random.hh @@ -90,6 +90,18 @@ struct RandomLibsodium_Impl using Random = ::dfi::crypto::libsodium::Random; Random random; }; + +#ifdef __DFI_PLUGIN_BITCOIN__ +//! \brief Bitcoin random implementation fixture +//! \since docker-finance 1.1.0 +struct RandomBitcoin_Impl +{ + protected: + using Random = ::dfi::crypto::bitcoin::Random; + Random random; +}; +#endif + } // namespace tests } // namespace dfi diff --git a/container/src/root/test/unit/random.hh b/container/src/root/test/unit/random.hh index b74e361..ae5c314 100644 --- a/container/src/root/test/unit/random.hh +++ b/container/src/root/test/unit/random.hh @@ -70,6 +70,11 @@ TEST_F(RandomInterface, generate_uint32_t) ASSERT_EQ(random.generate(), std::numeric_limits::max()); } +TEST_F(RandomInterface, generate_uint64_t) +{ + ASSERT_EQ(random.generate(), std::numeric_limits::max()); +} + TEST_F(RandomInterface, generate_int16_t) { ASSERT_EQ(random.generate(), std::numeric_limits::max()); @@ -96,9 +101,13 @@ struct RandomFixture : public ::testing::Test, protected t_impl auto two = t_impl::random.template generate(); static_assert(std::is_same_v); - // NOTE: t_random limits are implementation-specific - ASSERT_NEAR(0, one, std::numeric_limits::max()); - ASSERT_NEAR(0, two, std::numeric_limits::max()); + // TODO(unassigned): gtest implicit conversion of uint64_t with ASSERT_NEAR + if constexpr (!std::is_same_v) + { + // NOTE: t_random limits are implementation-specific + ASSERT_NEAR(0, one, std::numeric_limits::max()); + ASSERT_NEAR(0, two, std::numeric_limits::max()); + } // Exceedingly rare (tests the obvious, but is not an accurate entropy test) // If fails, re-run tests to confirm @@ -176,6 +185,28 @@ TEST_F(RandomLibsodium, generate_uint32_t) ASSERT_NO_THROW(generate()); } +#ifdef __DFI_PLUGIN_BITCOIN__ +// +// Bitcoin +// + +//! \brief Bitcoin random number fixture +//! \since docker-finance 1.1.0 +struct RandomBitcoin : public RandomFixture +{ +}; + +TEST_F(RandomBitcoin, generate_uint32_t) +{ + ASSERT_NO_THROW(generate()); +} + +TEST_F(RandomBitcoin, generate_uint64_t) +{ + ASSERT_NO_THROW(generate()); +} +#endif + } // namespace unit } // namespace tests } // namespace dfi