From 7b0a08f9ff94059d5bc4e07f2319e25e0ae3e090 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 01:57:40 +0000 Subject: [PATCH 01/10] Replace cmake/ with host-uk/build submodule at .core/build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build system extracted to host-uk/build and wired in as a git submodule following the core CLI convention (.core//). - CMake modules, cross-compilation profiles now from .core/build/cmake/ - Conan credentials read from environment instead of hardcoded - Removed cmake/ directory (14 files) — all now in submodule - Makefile, CMakeLists.txt, CMakePresets.json updated for new paths Co-Authored-By: Claude Opus 4.6 --- .core/build | 1 + .gitmodules | 3 + CMakeLists.txt | 2 +- CMakePresets.json | 4 +- Makefile | 59 +-- cmake/CPackConfig.cmake | 88 ---- cmake/CleanBuild.cmake | 76 ---- cmake/ConanGet.cmake | 102 ----- cmake/ConanProfileSetup.cmake | 38 -- cmake/ConanProvider.cmake | 698 ------------------------------ cmake/DocBuilder.cmake | 45 -- cmake/FindCcache.cmake | 8 - cmake/profiles/apple-clang-armv8 | 9 - cmake/profiles/apple-clang-x86_64 | 8 - cmake/profiles/gcc-linux-armv8 | 9 - cmake/profiles/gcc-linux-x86_64 | 9 - cmake/profiles/msvc-193-x86_64 | 10 - cmake/profiles/msvc-194-x86_64 | 8 - cmake/test-static-assert.c | 6 - 19 files changed, 37 insertions(+), 1146 deletions(-) create mode 160000 .core/build delete mode 100644 cmake/CPackConfig.cmake delete mode 100644 cmake/CleanBuild.cmake delete mode 100644 cmake/ConanGet.cmake delete mode 100644 cmake/ConanProfileSetup.cmake delete mode 100644 cmake/ConanProvider.cmake delete mode 100644 cmake/DocBuilder.cmake delete mode 100644 cmake/FindCcache.cmake delete mode 100644 cmake/profiles/apple-clang-armv8 delete mode 100644 cmake/profiles/apple-clang-x86_64 delete mode 100644 cmake/profiles/gcc-linux-armv8 delete mode 100644 cmake/profiles/gcc-linux-x86_64 delete mode 100644 cmake/profiles/msvc-193-x86_64 delete mode 100644 cmake/profiles/msvc-194-x86_64 delete mode 100644 cmake/test-static-assert.c diff --git a/.core/build b/.core/build new file mode 160000 index 00000000..9c9346d5 --- /dev/null +++ b/.core/build @@ -0,0 +1 @@ +Subproject commit 9c9346d5494688a78ea573f3bc0547ad12b9be2a diff --git a/.gitmodules b/.gitmodules index e522feeb..29514a7f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "docs"] path = docs url = https://github.com/letheanVPN/documentation.git +[submodule ".core/build"] + path = .core/build + url = http://forge.snider.dev:4000/host-uk/build.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 08ec3bdc..da6e936c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) list(INSERT CMAKE_MODULE_PATH 0 - "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/.core/build/cmake") if(POLICY CMP0043) cmake_policy(SET CMP0043 NEW) diff --git a/CMakePresets.json b/CMakePresets.json index 80247a7d..80a4f47e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -18,7 +18,7 @@ "cacheVariables": { "TESTNET": "ON", "CMAKE_BUILD_TYPE": "Release", - "CMAKE_PROJECT_TOP_LEVEL_INCLUDES":"cmake/ConanProvider.cmake" + "CMAKE_PROJECT_TOP_LEVEL_INCLUDES":".core/build/cmake/ConanProvider.cmake" } }, { @@ -30,7 +30,7 @@ "cacheVariables": { "TESTNET": "OFF", "CMAKE_BUILD_TYPE": "Release", - "CMAKE_PROJECT_TOP_LEVEL_INCLUDES":"cmake/ConanProvider.cmake" + "CMAKE_PROJECT_TOP_LEVEL_INCLUDES":".core/build/cmake/ConanProvider.cmake" } } ], diff --git a/Makefile b/Makefile index e4391e7b..e14b4bad 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,15 @@ # Distributed under the MIT/X11 software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# ------------------------------------------------------------ -# Detect the number of logical CPU cores – works on Linux, -# macOS, BSD, and Windows (both cmd.exe and PowerShell). -# ------------------------------------------------------------ +# ============================================================ +# Build system from host-uk/build submodule +# ============================================================ +BUILD_SYS := .core/build +CMAKE_DIR := $(BUILD_SYS)/cmake -# Default to “unknown” – will be overwritten below. +# ============================================================ +# Project Configuration +# ============================================================ CPU_CORES := 1 TESTNET:= 0 STATIC:= 0 @@ -19,21 +22,22 @@ BUILD_FOLDER:=build/release PRESET_BUILD:=conan-release PRESET_CONFIGURE:=conan-release +# ------------------------------------------------------------ +# Detect the number of logical CPU cores – works on Linux, +# macOS, BSD, and Windows (both cmd.exe and PowerShell). +# ------------------------------------------------------------ UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown) ifeq ($(UNAME_S),Linux) - # Linux: try nproc first, fall back to /proc CPU_CORES := $(shell nproc 2>/dev/null || \ grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 1) endif ifeq ($(UNAME_S),Darwin) - # macOS: sysctl reports the number of logical CPUs CPU_CORES := $(shell sysctl -n hw.logicalcpu 2>/dev/null || echo 1) endif ifeq ($(filter %BSD,$(UNAME_S)),%BSD) - # *BSD: also sysctl, but the key differs on some variants CPU_CORES := $(shell sysctl -n hw.ncpu 2>/dev/null || echo 1) endif @@ -41,29 +45,27 @@ ifeq ($(OS),Windows_NT) PRESET_CONFIGURE:=conan-default - # Prefer the environment variable that Windows sets for us. - # It works in both cmd.exe and PowerShell. CPU_CORES := $(NUMBER_OF_PROCESSORS) - # If for some reason the env var is empty, fall back to PowerShell. ifeq ($(CPU_CORES),) CPU_CORES := $(shell powershell -NoProfile -Command ^ "[Environment]::ProcessorCount") endif endif -# ----------------------------------------------------------------- -# Safety net – ensure we always have a positive integer. -# ----------------------------------------------------------------- +# Safety net CPU_CORES := $(or $(CPU_CORES),1) CPU_CORES := $(shell expr $(CPU_CORES) + 0 2>/dev/null || echo 1) CONAN_CPU_COUNT=$(CPU_CORES) -PROFILES :=$(patsubst cmake/profiles/%,%,$(wildcard cmake/profiles/*)) +# ============================================================ +# Paths — profiles and cmake modules from .build submodule +# ============================================================ +PROFILES :=$(patsubst $(CMAKE_DIR)/profiles/%,%,$(wildcard $(CMAKE_DIR)/profiles/*)) SORTED_PROFILES :=$(sort $(PROFILES)) CONAN_CACHE :=$(CURDIR)/build/sdk -CONAN_URL :=https://artifacts.host.uk.com/artifactory/api/conan/conan-build -CONAN_USER :=public -CONAN_PASSWORD :=Lethean1234 +CONAN_URL :=http://forge.snider.dev:4000/api/packages/host-uk/conan +CONAN_USER ?=$(shell echo $$CONAN_USER) +CONAN_PASSWORD ?=$(shell echo $$CONAN_PASSWORD) CONAN_EXECUTABLE :=$(CURDIR)/build/bin/conan CC_DOCKER_FILE ?=utils/docker/images/lthn-chain/Dockerfile SDK_PACKAGES_JSON :=$(wildcard utils/sdk/packages/*.json) @@ -111,10 +113,10 @@ docs: configure sdk: $(MAKE) -C utils/sdk $(filter-out $@,$(MAKECMDGOALS)) PACKAGE_VERSION=$(BUILD_VERSION) -# Rule for each profile +# Rule for each profile — uses .build/cmake/profiles/ $(PROFILES): conan-profile-detect @echo "Building profile: $@" - CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) install . -pr:h=cmake/profiles/$@ --build=missing -s build_type=$(BUILD_TYPE) + CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) install . -pr:h=$(CMAKE_DIR)/profiles/$@ --build=missing -s build_type=$(BUILD_TYPE) cmake -S . -B $(BUILD_FOLDER) -DCMAKE_TOOLCHAIN_FILE=$(BUILD_FOLDER)/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DSTATIC=$(STATIC) -DTESTNET=$(TESTNET) -DBUILD_VERSION=$(BUILD_VERSION) cmake --build $(BUILD_FOLDER) --config=$(BUILD_TYPE) --parallel=$(CPU_CORES) (cd $(BUILD_FOLDER) && cpack) @@ -128,10 +130,10 @@ help: @echo "" @echo "Available targets:" @printf " %-42s %s\n" "make clean" "Clean all build directories" - @printf " %-42s %s\n" "make get-conan" "Download and install conan locally" + @printf " %-42s %s\n" "make conan-get" "Download and install conan locally" @printf " %-42s %s\n" "make release" "Build release" - @printf " %-42s %s\n" "make static" "Build static release" - @printf " %-42s %s\n" "make debug" "Build debug" + @printf " %-42s %s\n" "make testnet" "Build testnet" + @printf " %-42s %s\n" "make mainnet" "Build mainnet" @printf " %-42s %s\n" "make test" "Build & run tests" @printf " %-42s %s\n" "make docs" "Builds offline documentation website" @printf " %-42s %s\n" "make docs-dev" "Runs local doc server, for editing/adding docs" @@ -161,9 +163,9 @@ test-debug: cmake --build build/test-debug --config=Debug --parallel=$(CPU_CORES) $(MAKE) test -# allowing this target to error quietly saves cross brwoser file detection +# Conan management — cmake modules from .build submodule conan-get: - cmake -P cmake/ConanGet.cmake + cmake -P $(CMAKE_DIR)/ConanGet.cmake (CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote add conan_build $(CONAN_URL) && \ CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote login conan_build $(CONAN_USER) -p $(CONAN_PASSWORD)) || true @@ -171,7 +173,7 @@ conan-upload: CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) upload "*" -r=conan_build --confirm conan-profile-detect: conan-get - cmake -P cmake/ConanProfileSetup.cmake + cmake -P $(CMAKE_DIR)/ConanProfileSetup.cmake docs-dev: configure @echo "Building Documentation" @@ -181,7 +183,7 @@ $(SDK_TARGETS): @# This is a proxy target. Handled by the 'sdk' rule. clean: - @cmake -P cmake/CleanBuild.cmake + @cmake -P $(CMAKE_DIR)/CleanBuild.cmake clean-build: clean rm -rf build @@ -189,5 +191,4 @@ clean-build: clean tags: ctags -R --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ src contrib tests/gtest -.PHONY: all release upload-conan-cache docs docs-dev configure static static-release test test-release test-debug clean tags conan-profile-detect get-conan $(PROFILES) sdk $(SDK_TARGETS) -.PHONY: go-client \ No newline at end of file +.PHONY: all release upload-conan-cache docs docs-dev configure static static-release test test-release test-debug clean tags conan-profile-detect conan-get $(PROFILES) sdk $(SDK_TARGETS) diff --git a/cmake/CPackConfig.cmake b/cmake/CPackConfig.cmake deleted file mode 100644 index 9890ad10..00000000 --- a/cmake/CPackConfig.cmake +++ /dev/null @@ -1,88 +0,0 @@ - - -if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "") - set(CPACK_PACKAGE_NAME "${package_name}") - set(CPACK_PACKAGE_VENDOR "${package_vendor}") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${package_description}") - set(CPACK_PACKAGE_VERSION "${BUILD_VERSION}") - set(CPACK_PACKAGE_CONTACT "${package_contact}") - set(CPACK_PACKAGE_HOMEPAGE_URL "${package_website}") - - #set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") # Linux/macOS default - #if(WIN32) - # set(CPACK_PACKAGING_INSTALL_PREFIX "C:/Program Files/${PROJECT_NAME}") - #endif() - - if(APPLE) - if("${package_macos_installer}" STREQUAL "DMG") - # set(CPACK_GENERATOR "DragNDrop") -# set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/resources/dmg_background.png") - # set(CPACK_DMG_VOLUME_NAME "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}") - # message(STATUS "Registered CPACK_GENERATOR: DragNDrop") - else () - # set(CPACK_GENERATOR "productbuild") - # set(CPACK_PRODUCTBUILD_IDENTIFIER "${package_macos_pkg_productbuild_identifier}") -# set(CPACK_PRODUCTBUILD_SIGNING_IDENTITY "Developer ID Installer: Your Company (TEAMID)") - # message(STATUS "Registered CPACK_GENERATOR: productbuild") - endif () - elseif(WIN32) - set(CPACK_GENERATOR "NSIS") - set(CPACK_NSIS_MODIFY_PATH ON) - set(CPACK_NSIS_HELP_LINK "https://lt.hn/getting-started/chain.html") - set(CPACK_NSIS_URL_INFO_ABOUT "https://lt.hn") - set(CPACK_NSIS_CONTACT "support@lt.hn") - set(CPACK_NSIS_UNINSTALL_NAME "Lethean CLI") - set(CPACK_NSIS_BRANDING_TEXT "Lethean Community") - set(CPACK_NSIS_MUI_FINISHPAGE_RUN "lethean-testnet-chain-node.exe") - - message(STATUS "Registered CPACK_GENERATOR: Inno exe") -# set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/resources/windows_icon.ico") -# set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/LICENSE.rtf") -# set(CPACK_WIX_UPGRADE_GUID "D3F5A9C1-4B2E-4F5A-9C71-123456789ABC") # change once per major version - else() - set(CPACK_GENERATOR "DEB") - set(CPACK_GENERATOR "RPM") - message(STATUS "Registered CPACK_GENERATOR: deb") - set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${package_contact}") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.28)") - set(CPACK_DEBIAN_PACKAGE_SECTION "utils") - set(CPACK_DEBIAN_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") - # post‑install script (e.g., to register a systemd service) -# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA -# "${CMAKE_SOURCE_DIR}/scripts/postinstall.sh") - endif() - - list(APPEND CPACK_GENERATOR "TXZ" "ZIP") - message(STATUS "Registered CPACK_GENERATOR: tgz") - message(STATUS "Registered CPACK_GENERATOR: zip") - set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) - set(CPACK_ARCHIVE_COMPONENT_INSTALL OFF) - set(CPACK_COMPONENTS_ALL) - set(CPACK_MONOLITHIC_INSTALL OFF) - set(CPACK_PACKAGE_CHECKSUM SHA256) - message(STATUS "Using SHA256 Checksums") - - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt") - set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") - - set(CPACK_PACKAGE_DIRECTORY "${CMAKE_SOURCE_DIR}/build/packages") - - install(FILES README.md LICENSE.txt - DESTINATION "share/doc/${PROJECT_NAME}" - ) - - set(_arch "${CMAKE_SYSTEM_PROCESSOR}") - - if(_arch MATCHES "AMD64|x86_64") - set(_arch "x86_64") - elseif(_arch MATCHES "arm64|aarch64") - set(_arch "arm64") - endif() - - string(TOLOWER "${CMAKE_SYSTEM_NAME}" _sys_name_lc) - - set(CPACK_PACKAGE_FILE_NAME - "${package_name}-${CPACK_PACKAGE_VERSION}-${_sys_name_lc}-${_arch}") - - include(CPack) -endif() \ No newline at end of file diff --git a/cmake/CleanBuild.cmake b/cmake/CleanBuild.cmake deleted file mode 100644 index e9f3ab79..00000000 --- a/cmake/CleanBuild.cmake +++ /dev/null @@ -1,76 +0,0 @@ -# cmake/CleanBuild.cmake - -# Function to selectively clean the build directory. -# -# This function will remove most of the generated build files from the build -# directory, while preserving specific directories that contain downloaded -# tools or generated documentation. -# -# Golden Rules: -# - never delete build/ -# - never delete build/bin -# - never delete build/docs -# - never delete build/sdk (non cache) -# -# It will: -# - purge build files -# - clean up conan build files, cached sources can always remain. -function(selective_clean_build_dir) - if(EXISTS "${CMAKE_SOURCE_DIR}/build") - message(STATUS "Selectively cleaning build directory: ${CMAKE_SOURCE_DIR}/build") - - # List of top-level items in the build directory to keep. - set(golden_items - "${CMAKE_SOURCE_DIR}/build/.ccache" - "${CMAKE_SOURCE_DIR}/build/bin" - "${CMAKE_SOURCE_DIR}/build/docs" - "${CMAKE_SOURCE_DIR}/build/sdk" - ) - - # Get all top-level items in the build directory. - file(GLOB top_level_items "${CMAKE_SOURCE_DIR}/build/*") - - foreach(item ${top_level_items}) - list(FIND golden_items "${item}" is_golden) - if(is_golden STREQUAL "-1") - string(FIND "${item}" "${CMAKE_SOURCE_DIR}/build" is_prefixed) - if(is_prefixed EQUAL 0) - message(STATUS "Removing: ${item}") - if(IS_DIRECTORY "${item}") - file(REMOVE_RECURSE "${item}") - else() - file(REMOVE "${item}") - endif() - else() - message(WARNING "Safety check failed: Will not remove '${item}' because it is not prefixed with CMAKE_SOURCE_DIR/build.") - endif() - else() - message(STATUS "Keeping golden item: ${item}") - endif() - endforeach() - - message(STATUS "Selective clean complete.") - else() - message(STATUS "Build directory not found, skipping clean.") - endif() -endfunction() - -function(reset_conan_presets) - set(CONAN_PRESETS_FILE "${CMAKE_SOURCE_DIR}/ConanPresets.json") - - set(NEW_CONTENT [[{ - "version": 4, - "vendor": { - "conan": {} - }, - "include": [ - - ] -}]]) - message(STATUS "Resetting ${CONAN_PRESETS_FILE} to a clean state.") - file(WRITE "${CONAN_PRESETS_FILE}" "${NEW_CONTENT}") - message(STATUS "${CONAN_PRESETS_FILE} has been successfully reset.") -endfunction() - -selective_clean_build_dir() -reset_conan_presets() diff --git a/cmake/ConanGet.cmake b/cmake/ConanGet.cmake deleted file mode 100644 index 6110328e..00000000 --- a/cmake/ConanGet.cmake +++ /dev/null @@ -1,102 +0,0 @@ -# cmake/ConanGet.cmake - -# This module downloads and installs Conan if it's not found. - -# Set the Conan version -set(CONAN_VERSION 2.21.0) - -# Set the download URLs -set(CONAN_URL_MACOS_ARM "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-macos-arm64.tgz") -set(CONAN_URL_MACOS_INTEL "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-macos-x86_64.tgz") -set(CONAN_URL_WINDOWS_X86_64 "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-windows-x86_64.zip") -set(CONAN_URL_WINDOWS_ARM64 "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-windows-arm64.zip") -set(CONAN_URL_LINUX_X86_64 "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-linux-x86_64.tgz") -set(CONAN_URL_LINUX_AARCH64 "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-linux-aarch64.tgz") - -# Set the installation directory -if(NOT CMAKE_BINARY_DIR) - set(CMAKE_BINARY_DIR "${CONAN_INSTALL_DIR}") -endif() -if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - set(CONAN_INSTALL_DIR "${CMAKE_SOURCE_DIR}/build/bin") - set(CONAN_EXECUTABLE "${CMAKE_SOURCE_DIR}/build/bin/conan.exe") -else () - set(CONAN_INSTALL_DIR "${CMAKE_SOURCE_DIR}/build") - set(CONAN_EXECUTABLE "${CONAN_INSTALL_DIR}/bin/conan") -endif () -# Check if Conan is already installed -if(NOT EXISTS "${CONAN_EXECUTABLE}") - message(STATUS "Conan not found. Downloading and installing...") - - file(MAKE_DIRECTORY "${CONAN_INSTALL_DIR}") - - # Determine the processor architecture, with a fallback - if(CMAKE_HOST_SYSTEM_PROCESSOR) - set(HOST_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) - else() - cmake_host_system_information(RESULT HOST_PROCESSOR QUERY OS_PLATFORM) - endif() - - # Detect the operating system and architecture - message(STATUS "Detecting OS and architecture: ${HOST_PROCESSOR} on ${CMAKE_HOST_SYSTEM_NAME}") - if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") - if(HOST_PROCESSOR MATCHES "arm64") - set(CONAN_URL ${CONAN_URL_MACOS_ARM}) - set(CONAN_ARCHIVE_TYPE "tgz") - else() - set(CONAN_URL ${CONAN_URL_MACOS_INTEL}) - set(CONAN_ARCHIVE_TYPE "tgz") - endif() - elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - if(HOST_PROCESSOR MATCHES "ARM64") - set(CONAN_URL ${CONAN_URL_WINDOWS_ARM64}) - set(CONAN_ARCHIVE_TYPE "zip") - else() - set(CONAN_URL ${CONAN_URL_WINDOWS_X86_64}) - set(CONAN_ARCHIVE_TYPE "zip") - endif() - elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - if(HOST_PROCESSOR MATCHES "aarch64|arm64|ARM64") - set(CONAN_URL ${CONAN_URL_LINUX_AARCH64}) - set(CONAN_ARCHIVE_TYPE "tgz") - else() - set(CONAN_URL ${CONAN_URL_LINUX_X86_64}) - set(CONAN_ARCHIVE_TYPE "tgz") - endif() - else() - message(FATAL_ERROR "Unsupported operating system: ${CMAKE_HOST_SYSTEM_NAME}") - endif() - - # Download and extract Conan - set(CONAN_ARCHIVE "${CMAKE_BINARY_DIR}/conan.${CONAN_ARCHIVE_TYPE}") - - message(STATUS "Downloading ${CONAN_URL} to ${CONAN_ARCHIVE}") - file(DOWNLOAD "${CONAN_URL}" "${CONAN_ARCHIVE}" SHOW_PROGRESS) - - message(STATUS "Extracting ${CONAN_ARCHIVE} to ${CONAN_INSTALL_DIR}") - if(CONAN_ARCHIVE_TYPE STREQUAL "tgz") - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xzf "${CONAN_ARCHIVE}" - WORKING_DIRECTORY "${CONAN_INSTALL_DIR}" - RESULT_VARIABLE result - ) - elseif(CONAN_ARCHIVE_TYPE STREQUAL "zip") - # CMake -E tar can handle zip files - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xf "${CONAN_ARCHIVE}" - WORKING_DIRECTORY "${CONAN_INSTALL_DIR}" - RESULT_VARIABLE result - ) - endif() - - if(NOT result EQUAL 0) - message(FATAL_ERROR "Failed to extract Conan archive.") - endif() - - # Clean up the archive - file(REMOVE "${CONAN_ARCHIVE}") - - message(STATUS "Conan installed successfully at ${CONAN_EXECUTABLE}") -else() - message(STATUS "Conan already installed at ${CONAN_EXECUTABLE}") -endif() diff --git a/cmake/ConanProfileSetup.cmake b/cmake/ConanProfileSetup.cmake deleted file mode 100644 index c6a0e803..00000000 --- a/cmake/ConanProfileSetup.cmake +++ /dev/null @@ -1,38 +0,0 @@ -set(CONAN_HOME "${CMAKE_SOURCE_DIR}/build/sdk") -set(DEFAULT_PROFILE "${CONAN_HOME}/profiles/default") -if(WIN32) - set(CONAN_EXECUTABLE "${CMAKE_SOURCE_DIR}/build/bin/conan.exe") -else() - set(CONAN_EXECUTABLE "${CMAKE_SOURCE_DIR}/build/bin/conan") -endif() -if(NOT EXISTS "${DEFAULT_PROFILE}") - message(STATUS "Conan default profile not found. Detecting a new one...") - set(ENV{CONAN_HOME} "${CONAN_HOME}") - execute_process( - COMMAND "${CONAN_EXECUTABLE}" profile detect --name=default --force - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE return_code - ) - unset(ENV{CONAN_HOME}) - - if(NOT return_code EQUAL 0) - message(FATAL_ERROR "Conan profile detection failed with exit code: ${return_code}") - endif() -endif() - -message(STATUS "Appending custom settings to Conan default profile...") - -set(CUSTOM_SETTINGS " -compiler.cppstd=17 -") - -#if(WIN32) -# message(STATUS "Windows detected. Appending static runtime setting.") -# string(APPEND CUSTOM_SETTINGS " -#compiler.runtime=static -#") -#endif() - -file(APPEND "${DEFAULT_PROFILE}" "${CUSTOM_SETTINGS}") - -message(STATUS "Conan profile setup is complete.") \ No newline at end of file diff --git a/cmake/ConanProvider.cmake b/cmake/ConanProvider.cmake deleted file mode 100644 index 036ede6e..00000000 --- a/cmake/ConanProvider.cmake +++ /dev/null @@ -1,698 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2024 JFrog -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -set(CONAN_MINIMUM_VERSION 2.0.5) - -# Create a new policy scope and set the minimum required cmake version so the -# features behind a policy setting like if(... IN_LIST ...) behaves as expected -# even if the parent project does not specify a minimum cmake version or a minimum -# version less than this module requires (e.g. 3.0) before the first project() call. -# (see: https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.html) -# -# The policy-affecting calls like cmake_policy(SET...) or `cmake_minimum_required` only -# affects the current policy scope, i.e. between the PUSH and POP in this case. -# -# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Policies.html#the-policy-stack -cmake_policy(PUSH) -cmake_minimum_required(VERSION 3.24) - - -function(detect_os os os_api_level os_sdk os_subsystem os_version) - # it could be cross compilation - message(STATUS "CMake-Conan: cmake_system_name=${CMAKE_SYSTEM_NAME}") - if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(${os} Macos PARENT_SCOPE) - elseif(CMAKE_SYSTEM_NAME STREQUAL "QNX") - set(${os} Neutrino PARENT_SCOPE) - elseif(CMAKE_SYSTEM_NAME STREQUAL "CYGWIN") - set(${os} Windows PARENT_SCOPE) - set(${os_subsystem} cygwin PARENT_SCOPE) - elseif(CMAKE_SYSTEM_NAME MATCHES "^MSYS") - set(${os} Windows PARENT_SCOPE) - set(${os_subsystem} msys2 PARENT_SCOPE) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - # https://github.com/emscripten-core/emscripten/blob/4.0.6/cmake/Modules/Platform/Emscripten.cmake#L17C1-L17C34 - set(${os} Emscripten PARENT_SCOPE) - else() - set(${os} ${CMAKE_SYSTEM_NAME} PARENT_SCOPE) - endif() - if(CMAKE_SYSTEM_NAME STREQUAL "Android") - if(DEFINED ANDROID_PLATFORM) - string(REGEX MATCH "[0-9]+" _os_api_level ${ANDROID_PLATFORM}) - elseif(DEFINED CMAKE_SYSTEM_VERSION) - set(_os_api_level ${CMAKE_SYSTEM_VERSION}) - endif() - message(STATUS "CMake-Conan: android api level=${_os_api_level}") - set(${os_api_level} ${_os_api_level} PARENT_SCOPE) - endif() - if(CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS") - # CMAKE_OSX_SYSROOT contains the full path to the SDK for MakeFile/Ninja - # generators, but just has the original input string for Xcode. - if(NOT IS_DIRECTORY ${CMAKE_OSX_SYSROOT}) - set(_os_sdk ${CMAKE_OSX_SYSROOT}) - else() - if(CMAKE_OSX_SYSROOT MATCHES Simulator) - set(apple_platform_suffix simulator) - else() - set(apple_platform_suffix os) - endif() - if(CMAKE_OSX_SYSROOT MATCHES AppleTV) - set(_os_sdk "appletv${apple_platform_suffix}") - elseif(CMAKE_OSX_SYSROOT MATCHES iPhone) - set(_os_sdk "iphone${apple_platform_suffix}") - elseif(CMAKE_OSX_SYSROOT MATCHES Watch) - set(_os_sdk "watch${apple_platform_suffix}") - endif() - endif() - if(DEFINED os_sdk) - message(STATUS "CMake-Conan: cmake_osx_sysroot=${CMAKE_OSX_SYSROOT}") - set(${os_sdk} ${_os_sdk} PARENT_SCOPE) - endif() - if(DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) - message(STATUS "CMake-Conan: cmake_osx_deployment_target=${CMAKE_OSX_DEPLOYMENT_TARGET}") - set(${os_version} ${CMAKE_OSX_DEPLOYMENT_TARGET} PARENT_SCOPE) - endif() - endif() - endif() -endfunction() - - -function(detect_arch arch) - # CMAKE_OSX_ARCHITECTURES can contain multiple architectures, but Conan only supports one. - # Therefore this code only finds one. If the recipes support multiple architectures, the - # build will work. Otherwise, there will be a linker error for the missing architecture(s). - if(DEFINED CMAKE_OSX_ARCHITECTURES) - string(REPLACE " " ";" apple_arch_list "${CMAKE_OSX_ARCHITECTURES}") - list(LENGTH apple_arch_list apple_arch_count) - if(apple_arch_count GREATER 1) - message(WARNING "CMake-Conan: Multiple architectures detected, this will only work if Conan recipe(s) produce fat binaries.") - endif() - endif() - if(CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS" AND NOT CMAKE_OSX_ARCHITECTURES STREQUAL "") - set(host_arch ${CMAKE_OSX_ARCHITECTURES}) - elseif(MSVC) - set(host_arch ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}) - else() - set(host_arch ${CMAKE_SYSTEM_PROCESSOR}) - endif() - if(host_arch MATCHES "aarch64|arm64|ARM64") - set(_arch armv8) - elseif(host_arch MATCHES "armv7|armv7-a|armv7l|ARMV7") - set(_arch armv7) - elseif(host_arch MATCHES armv7s) - set(_arch armv7s) - elseif(host_arch MATCHES "i686|i386|X86") - set(_arch x86) - elseif(host_arch MATCHES "AMD64|amd64|x86_64|x64") - set(_arch x86_64) - endif() - if(EMSCRIPTEN) - # https://github.com/emscripten-core/emscripten/blob/4.0.6/cmake/Modules/Platform/Emscripten.cmake#L294C1-L294C80 - set(_arch wasm) - endif() - message(STATUS "CMake-Conan: cmake_system_processor=${_arch}") - set(${arch} ${_arch} PARENT_SCOPE) -endfunction() - - -function(detect_cxx_standard cxx_standard) - set(${cxx_standard} ${CMAKE_CXX_STANDARD} PARENT_SCOPE) - if(CMAKE_CXX_EXTENSIONS) - set(${cxx_standard} "gnu${CMAKE_CXX_STANDARD}" PARENT_SCOPE) - endif() -endfunction() - - -macro(detect_gnu_libstdcxx) - # _conan_is_gnu_libstdcxx true if GNU libstdc++ - check_cxx_source_compiles(" - #include - #if !defined(__GLIBCXX__) && !defined(__GLIBCPP__) - static_assert(false); - #endif - int main(){}" _conan_is_gnu_libstdcxx) - - # _conan_gnu_libstdcxx_is_cxx11_abi true if C++11 ABI - check_cxx_source_compiles(" - #include - static_assert(sizeof(std::string) != sizeof(void*), \"using libstdc++\"); - int main () {}" _conan_gnu_libstdcxx_is_cxx11_abi) - - set(_conan_gnu_libstdcxx_suffix "") - if(_conan_gnu_libstdcxx_is_cxx11_abi) - set(_conan_gnu_libstdcxx_suffix "11") - endif() - unset (_conan_gnu_libstdcxx_is_cxx11_abi) -endmacro() - - -macro(detect_libcxx) - # _conan_is_libcxx true if LLVM libc++ - check_cxx_source_compiles(" - #include - #if !defined(_LIBCPP_VERSION) - static_assert(false); - #endif - int main(){}" _conan_is_libcxx) -endmacro() - - -function(detect_lib_cxx lib_cxx) - if(CMAKE_SYSTEM_NAME STREQUAL "Android") - message(STATUS "CMake-Conan: android_stl=${CMAKE_ANDROID_STL_TYPE}") - set(${lib_cxx} ${CMAKE_ANDROID_STL_TYPE} PARENT_SCOPE) - return() - endif() - - include(CheckCXXSourceCompiles) - - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - detect_gnu_libstdcxx() - set(${lib_cxx} "libstdc++${_conan_gnu_libstdcxx_suffix}" PARENT_SCOPE) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") - set(${lib_cxx} "libc++" PARENT_SCOPE) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_SYSTEM_NAME MATCHES "Windows") - # Check for libc++ - detect_libcxx() - if(_conan_is_libcxx) - set(${lib_cxx} "libc++" PARENT_SCOPE) - return() - endif() - - # Check for libstdc++ - detect_gnu_libstdcxx() - if(_conan_is_gnu_libstdcxx) - set(${lib_cxx} "libstdc++${_conan_gnu_libstdcxx_suffix}" PARENT_SCOPE) - return() - endif() - - # TODO: it would be an error if we reach this point - elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - # Do nothing - compiler.runtime and compiler.runtime_type - # should be handled separately: https://github.com/conan-io/cmake-conan/pull/516 - return() - else() - # TODO: unable to determine, ask user to provide a full profile file instead - endif() -endfunction() - - -function(detect_compiler compiler compiler_version compiler_runtime compiler_runtime_type) - if(DEFINED CMAKE_CXX_COMPILER_ID) - set(_compiler ${CMAKE_CXX_COMPILER_ID}) - set(_compiler_version ${CMAKE_CXX_COMPILER_VERSION}) - else() - if(NOT DEFINED CMAKE_C_COMPILER_ID) - message(FATAL_ERROR "C or C++ compiler not defined") - endif() - set(_compiler ${CMAKE_C_COMPILER_ID}) - set(_compiler_version ${CMAKE_C_COMPILER_VERSION}) - endif() - - message(STATUS "CMake-Conan: CMake compiler=${_compiler}") - message(STATUS "CMake-Conan: CMake compiler version=${_compiler_version}") - - if(_compiler MATCHES MSVC) - set(_compiler "msvc") - string(SUBSTRING ${MSVC_VERSION} 0 3 _compiler_version) - # Configure compiler.runtime and compiler.runtime_type settings for MSVC - if(CMAKE_MSVC_RUNTIME_LIBRARY) - set(_msvc_runtime_library ${CMAKE_MSVC_RUNTIME_LIBRARY}) - else() - set(_msvc_runtime_library MultiThreaded$<$:Debug>DLL) # default value documented by CMake - endif() - - set(_KNOWN_MSVC_RUNTIME_VALUES "") - list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded MultiThreadedDLL) - list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreadedDebug MultiThreadedDebugDLL) - list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded$<$:Debug> MultiThreaded$<$:Debug>DLL) - - # only accept the 6 possible values, otherwise we don't don't know to map this - if(NOT _msvc_runtime_library IN_LIST _KNOWN_MSVC_RUNTIME_VALUES) - message(FATAL_ERROR "CMake-Conan: unable to map MSVC runtime: ${_msvc_runtime_library} to Conan settings") - endif() - - # Runtime is "dynamic" in all cases if it ends in DLL - if(_msvc_runtime_library MATCHES ".*DLL$") - set(_compiler_runtime "dynamic") - else() - set(_compiler_runtime "static") - endif() - message(STATUS "CMake-Conan: CMake compiler.runtime=${_compiler_runtime}") - - # Only define compiler.runtime_type when explicitly requested - # If a generator expression is used, let Conan handle it conditional on build_type - if(NOT _msvc_runtime_library MATCHES ":Debug>") - if(_msvc_runtime_library MATCHES "Debug") - set(_compiler_runtime_type "Debug") - else() - set(_compiler_runtime_type "Release") - endif() - message(STATUS "CMake-Conan: CMake compiler.runtime_type=${_compiler_runtime_type}") - endif() - - unset(_KNOWN_MSVC_RUNTIME_VALUES) - - elseif(_compiler MATCHES AppleClang) - set(_compiler "apple-clang") - string(REPLACE "." ";" VERSION_LIST ${_compiler_version}) - list(GET VERSION_LIST 0 _compiler_version) - elseif(_compiler MATCHES Clang) - set(_compiler "clang") - string(REPLACE "." ";" VERSION_LIST ${_compiler_version}) - list(GET VERSION_LIST 0 _compiler_version) - elseif(_compiler MATCHES GNU) - set(_compiler "gcc") - string(REPLACE "." ";" VERSION_LIST ${_compiler_version}) - list(GET VERSION_LIST 0 _compiler_version) - endif() - - message(STATUS "CMake-Conan: [settings] compiler=${_compiler}") - message(STATUS "CMake-Conan: [settings] compiler.version=${_compiler_version}") - if (_compiler_runtime) - message(STATUS "CMake-Conan: [settings] compiler.runtime=${_compiler_runtime}") - endif() - if (_compiler_runtime_type) - message(STATUS "CMake-Conan: [settings] compiler.runtime_type=${_compiler_runtime_type}") - endif() - - set(${compiler} ${_compiler} PARENT_SCOPE) - set(${compiler_version} ${_compiler_version} PARENT_SCOPE) - set(${compiler_runtime} ${_compiler_runtime} PARENT_SCOPE) - set(${compiler_runtime_type} ${_compiler_runtime_type} PARENT_SCOPE) -endfunction() - - -function(detect_build_type build_type) - get_property(multiconfig_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - if(NOT multiconfig_generator) - # Only set when we know we are in a single-configuration generator - # Note: we may want to fail early if `CMAKE_BUILD_TYPE` is not defined - set(${build_type} ${CMAKE_BUILD_TYPE} PARENT_SCOPE) - endif() -endfunction() - - -macro(set_conan_compiler_if_appleclang lang command output_variable) - if(CMAKE_${lang}_COMPILER_ID STREQUAL "AppleClang") - execute_process(COMMAND xcrun --find ${command} - OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE) - cmake_path(GET _xcrun_out PARENT_PATH _xcrun_toolchain_path) - cmake_path(GET CMAKE_${lang}_COMPILER PARENT_PATH _compiler_parent_path) - if ("${_xcrun_toolchain_path}" STREQUAL "${_compiler_parent_path}") - set(${output_variable} "") - endif() - unset(_xcrun_out) - unset(_xcrun_toolchain_path) - unset(_compiler_parent_path) - endif() -endmacro() - - -macro(append_compiler_executables_configuration) - set(_conan_c_compiler "") - set(_conan_cpp_compiler "") - set(_conan_rc_compiler "") - set(_conan_compilers_list "") - if(CMAKE_C_COMPILER) - set(_conan_c_compiler "\"c\":\"${CMAKE_C_COMPILER}\"") - set_conan_compiler_if_appleclang(C cc _conan_c_compiler) - list(APPEND _conan_compilers_list ${_conan_c_compiler}) - else() - message(WARNING "CMake-Conan: The C compiler is not defined. " - "Please define CMAKE_C_COMPILER or enable the C language.") - endif() - if(CMAKE_CXX_COMPILER) - set(_conan_cpp_compiler "\"cpp\":\"${CMAKE_CXX_COMPILER}\"") - set_conan_compiler_if_appleclang(CXX c++ _conan_cpp_compiler) - list(APPEND _conan_compilers_list ${_conan_cpp_compiler}) - else() - message(WARNING "CMake-Conan: The C++ compiler is not defined. " - "Please define CMAKE_CXX_COMPILER or enable the C++ language.") - endif() - if(CMAKE_RC_COMPILER) - set(_conan_rc_compiler "\"rc\":\"${CMAKE_RC_COMPILER}\"") - list(APPEND _conan_compilers_list ${_conan_rc_compiler}) - # Not necessary to warn if RC not defined - endif() - if(NOT "x${_conan_compilers_list}" STREQUAL "x") - string(REPLACE ";" "," _conan_compilers_list "${_conan_compilers_list}") - string(APPEND profile "tools.build:compiler_executables={${_conan_compilers_list}}\n") - endif() - unset(_conan_c_compiler) - unset(_conan_cpp_compiler) - unset(_conan_rc_compiler) - unset(_conan_compilers_list) -endmacro() - - -function(detect_host_profile output_file) - detect_os(os os_api_level os_sdk os_subsystem os_version) - detect_arch(arch) - detect_compiler(compiler compiler_version compiler_runtime compiler_runtime_type) - detect_cxx_standard(compiler_cppstd) - detect_lib_cxx(compiler_libcxx) - detect_build_type(build_type) - - set(profile "") - string(APPEND profile "[settings]\n") - if(arch) - string(APPEND profile arch=${arch} "\n") - endif() - if(os) - string(APPEND profile os=${os} "\n") - endif() - if(os_api_level) - string(APPEND profile os.api_level=${os_api_level} "\n") - endif() - if(os_version) - string(APPEND profile os.version=${os_version} "\n") - endif() - if(os_sdk) - string(APPEND profile os.sdk=${os_sdk} "\n") - endif() - if(os_subsystem) - string(APPEND profile os.subsystem=${os_subsystem} "\n") - endif() - if(compiler) - string(APPEND profile compiler=${compiler} "\n") - endif() - if(compiler_version) - string(APPEND profile compiler.version=${compiler_version} "\n") - endif() - if(compiler_runtime) - string(APPEND profile compiler.runtime=${compiler_runtime} "\n") - endif() - if(compiler_runtime_type) - string(APPEND profile compiler.runtime_type=${compiler_runtime_type} "\n") - endif() - if(compiler_cppstd) - string(APPEND profile compiler.cppstd=${compiler_cppstd} "\n") - endif() - if(compiler_libcxx) - string(APPEND profile compiler.libcxx=${compiler_libcxx} "\n") - endif() - if(build_type) - string(APPEND profile "build_type=${build_type}\n") - endif() - - if(NOT DEFINED output_file) - set(file_name "${CMAKE_BINARY_DIR}/profile") - else() - set(file_name ${output_file}) - endif() - - string(APPEND profile "[conf]\n") - string(APPEND profile "tools.cmake.cmaketoolchain:generator=${CMAKE_GENERATOR}\n") - - # propagate compilers via profile - append_compiler_executables_configuration() - - if(os STREQUAL "Android") - string(APPEND profile "tools.android:ndk_path=${CMAKE_ANDROID_NDK}\n") - endif() - - message(STATUS "CMake-Conan: Creating profile ${file_name}") - file(WRITE ${file_name} ${profile}) - message(STATUS "CMake-Conan: Profile: \n${profile}") -endfunction() - - -function(conan_profile_detect_default) - message(STATUS "CMake-Conan: Checking if a default profile exists") - execute_process(COMMAND ${CONAN_COMMAND} profile path default - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - ECHO_OUTPUT_VARIABLE - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - if(NOT ${return_code} EQUAL "0") - message(STATUS "CMake-Conan: The default profile doesn't exist, detecting it.") - execute_process(COMMAND ${CONAN_COMMAND} profile detect - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - ECHO_OUTPUT_VARIABLE - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() -endfunction() - - -function(conan_install) - set(conan_output_folder ${CMAKE_BINARY_DIR}/conan) - # Invoke "conan install" with the provided arguments - set(conan_args ${conan_args} -of=${conan_output_folder}) - message(STATUS "CMake-Conan: conan install ${CMAKE_SOURCE_DIR} ${conan_args} ${ARGN}") - - - # In case there was not a valid cmake executable in the PATH, we inject the - # same we used to invoke the provider to the PATH - if(DEFINED PATH_TO_CMAKE_BIN) - set(old_path $ENV{PATH}) - set(ENV{PATH} "$ENV{PATH}:${PATH_TO_CMAKE_BIN}") - endif() - - execute_process(COMMAND ${CONAN_COMMAND} install ${CMAKE_SOURCE_DIR} ${conan_args} ${ARGN} --format=json - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - if(DEFINED PATH_TO_CMAKE_BIN) - set(ENV{PATH} "${old_path}") - endif() - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - - # the files are generated in a folder that depends on the layout used, if - # one is specified, but we don't know a priori where this is. - # TODO: this can be made more robust if Conan can provide this in the json output - string(JSON conan_generators_folder GET "${conan_stdout}" graph nodes 0 generators_folder) - cmake_path(CONVERT ${conan_generators_folder} TO_CMAKE_PATH_LIST conan_generators_folder) - - message(STATUS "CMake-Conan: CONAN_GENERATORS_FOLDER=${conan_generators_folder}") - set_property(GLOBAL PROPERTY CONAN_GENERATORS_FOLDER "${conan_generators_folder}") - # reconfigure on conanfile changes - string(JSON conanfile GET "${conan_stdout}" graph nodes 0 label) - message(STATUS "CMake-Conan: CONANFILE=${CMAKE_SOURCE_DIR}/${conanfile}") - set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/${conanfile}") - # success - set_property(GLOBAL PROPERTY CONAN_INSTALL_SUCCESS TRUE) - -endfunction() - - -function(conan_get_version conan_command conan_current_version) - execute_process( - COMMAND ${conan_command} --version - OUTPUT_VARIABLE conan_output - RESULT_VARIABLE conan_result - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(conan_result) - message(FATAL_ERROR "CMake-Conan: Error when trying to run Conan") - endif() - - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" conan_version ${conan_output}) - set(${conan_current_version} ${conan_version} PARENT_SCOPE) -endfunction() - - -function(conan_version_check) - set(options ) - set(one_value_args MINIMUM CURRENT) - set(multi_value_args ) - cmake_parse_arguments(conan_version_check - "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) - - if(NOT conan_version_check_MINIMUM) - message(FATAL_ERROR "CMake-Conan: Required parameter MINIMUM not set!") - endif() - if(NOT conan_version_check_CURRENT) - message(FATAL_ERROR "CMake-Conan: Required parameter CURRENT not set!") - endif() - - if(conan_version_check_CURRENT VERSION_LESS conan_version_check_MINIMUM) - message(FATAL_ERROR "CMake-Conan: Conan version must be ${conan_version_check_MINIMUM} or later") - endif() -endfunction() - - -macro(construct_profile_argument argument_variable profile_list) - set(${argument_variable} "") - if("${profile_list}" STREQUAL "CONAN_HOST_PROFILE") - set(_arg_flag "--profile:host=") - elseif("${profile_list}" STREQUAL "CONAN_BUILD_PROFILE") - set(_arg_flag "--profile:build=") - endif() - - set(_profile_list "${${profile_list}}") - list(TRANSFORM _profile_list REPLACE "auto-cmake" "${CMAKE_BINARY_DIR}/conan_host_profile") - list(TRANSFORM _profile_list PREPEND ${_arg_flag}) - set(${argument_variable} ${_profile_list}) - - unset(_arg_flag) - unset(_profile_list) -endmacro() - - -macro(conan_provide_dependency method package_name) - set_property(GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED TRUE) - get_property(_conan_install_success GLOBAL PROPERTY CONAN_INSTALL_SUCCESS) - if(NOT _conan_install_success) - if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - set(CONAN_COMMAND "${CMAKE_SOURCE_DIR}/build/bin/conan.exe") - else () - set(CONAN_COMMAND "${CMAKE_SOURCE_DIR}/build/bin/conan") - endif () - if(NOT EXISTS ${CONAN_COMMAND}) - message(STATUS "CMake-Conan: Local conan not found, attempting to download it.") - execute_process(COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_SOURCE_DIR}/cmake/ConanGet.cmake" - RESULT_VARIABLE result - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") - if(NOT result EQUAL 0) - message(FATAL_ERROR "Failed to download conan.") - endif() - endif() - if(NOT EXISTS ${CONAN_COMMAND}) - message(FATAL_ERROR "Conan executable not found at ${CONAN_COMMAND} after trying to download it. Also, make sure it has execution permissions.") - endif() - conan_get_version(${CONAN_COMMAND} CONAN_CURRENT_VERSION) - conan_version_check(MINIMUM ${CONAN_MINIMUM_VERSION} CURRENT ${CONAN_CURRENT_VERSION}) - message(STATUS "CMake-Conan: first find_package() found. Installing dependencies with Conan") - if("default" IN_LIST CONAN_HOST_PROFILE OR "default" IN_LIST CONAN_BUILD_PROFILE) - conan_profile_detect_default() - endif() - if("auto-cmake" IN_LIST CONAN_HOST_PROFILE) - detect_host_profile(${CMAKE_BINARY_DIR}/conan_host_profile) - endif() - construct_profile_argument(_host_profile_flags CONAN_HOST_PROFILE) - construct_profile_argument(_build_profile_flags CONAN_BUILD_PROFILE) - if(EXISTS "${CMAKE_SOURCE_DIR}/conanfile.py") - file(READ "${CMAKE_SOURCE_DIR}/conanfile.py" outfile) - if(NOT "${outfile}" MATCHES ".*CMakeDeps.*") - message(WARNING "Cmake-conan: CMakeDeps generator was not defined in the conanfile") - endif() - set(generator "") - elseif (EXISTS "${CMAKE_SOURCE_DIR}/conanfile.txt") - file(READ "${CMAKE_SOURCE_DIR}/conanfile.txt" outfile) - if(NOT "${outfile}" MATCHES ".*CMakeDeps.*") - message(WARNING "Cmake-conan: CMakeDeps generator was not defined in the conanfile. " - "Please define the generator as it will be mandatory in the future") - endif() - set(generator "-g;CMakeDeps") - endif() - get_property(_multiconfig_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - if(NOT _multiconfig_generator) - message(STATUS "CMake-Conan: Installing single configuration ${CMAKE_BUILD_TYPE}") - conan_install(${_host_profile_flags} ${_build_profile_flags} ${CONAN_INSTALL_ARGS} ${generator}) - else() - message(STATUS "CMake-Conan: Installing both Debug and Release") - conan_install(${_host_profile_flags} ${_build_profile_flags} -s build_type=Release ${CONAN_INSTALL_ARGS} ${generator}) - conan_install(${_host_profile_flags} ${_build_profile_flags} -s build_type=Debug ${CONAN_INSTALL_ARGS} ${generator}) - endif() - unset(_host_profile_flags) - unset(_build_profile_flags) - unset(_multiconfig_generator) - unset(_conan_install_success) - else() - message(STATUS "CMake-Conan: find_package(${ARGV1}) found, 'conan install' already ran") - unset(_conan_install_success) - endif() - - get_property(_conan_generators_folder GLOBAL PROPERTY CONAN_GENERATORS_FOLDER) - - # Ensure that we consider Conan-provided packages ahead of any other, - # irrespective of other settings that modify the search order or search paths - # This follows the guidelines from the find_package documentation - # (https://cmake.org/cmake/help/latest/command/find_package.html): - # find_package ( PATHS paths... NO_DEFAULT_PATH) - # find_package () - - # Filter out `REQUIRED` from the argument list, as the first call may fail - set(_find_args_${package_name} "${ARGN}") - list(REMOVE_ITEM _find_args_${package_name} "REQUIRED") - if(NOT "MODULE" IN_LIST _find_args_${package_name}) - find_package(${package_name} ${_find_args_${package_name}} BYPASS_PROVIDER PATHS "${_conan_generators_folder}" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) - unset(_find_args_${package_name}) - endif() - - # Invoke find_package a second time - if the first call succeeded, - # this will simply reuse the result. If not, fall back to CMake default search - # behaviour, also allowing modules to be searched. - if(NOT ${package_name}_FOUND) - list(FIND CMAKE_MODULE_PATH "${_conan_generators_folder}" _index) - if(_index EQUAL -1) - list(PREPEND CMAKE_MODULE_PATH "${_conan_generators_folder}") - endif() - unset(_index) - find_package(${package_name} ${ARGN} BYPASS_PROVIDER) - list(REMOVE_ITEM CMAKE_MODULE_PATH "${_conan_generators_folder}") - endif() -endmacro() - - -cmake_language( - SET_DEPENDENCY_PROVIDER conan_provide_dependency - SUPPORTED_METHODS FIND_PACKAGE -) - - -macro(conan_provide_dependency_check) - set(_conan_provide_dependency_invoked FALSE) - get_property(_conan_provide_dependency_invoked GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED) - if(NOT _conan_provide_dependency_invoked) - message(WARNING "Conan is correctly configured as dependency provider, " - "but Conan has not been invoked. Please add at least one " - "call to `find_package()`.") - if(DEFINED CONAN_COMMAND) - # supress warning in case `CONAN_COMMAND` was specified but unused. - set(_conan_command ${CONAN_COMMAND}) - unset(_conan_command) - endif() - endif() - unset(_conan_provide_dependency_invoked) -endmacro() - - -# Add a deferred call at the end of processing the top-level directory -# to check if the dependency provider was invoked at all. -cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL conan_provide_dependency_check) - -# Configurable variables for Conan profiles -set(CONAN_HOST_PROFILE "default;auto-cmake" CACHE STRING "Conan host profile") -set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile") -set(CONAN_INSTALL_ARGS "--build=missing" CACHE STRING "Command line arguments for conan install") - -find_program(_cmake_program NAMES cmake NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) -if(NOT _cmake_program) - get_filename_component(PATH_TO_CMAKE_BIN "${CMAKE_COMMAND}" DIRECTORY) - set(PATH_TO_CMAKE_BIN "${PATH_TO_CMAKE_BIN}" CACHE INTERNAL "Path where the CMake executable is") -endif() - -cmake_policy(POP) \ No newline at end of file diff --git a/cmake/DocBuilder.cmake b/cmake/DocBuilder.cmake deleted file mode 100644 index c02ad658..00000000 --- a/cmake/DocBuilder.cmake +++ /dev/null @@ -1,45 +0,0 @@ -set(MKDOCS_SRC "${CMAKE_SOURCE_DIR}/docs") -set(MKDOCS_OUT "${CMAKE_BINARY_DIR}/../docs") - -message("MKDocs src: ${MKDOCS_SRC} > ${MKDOCS_OUT}") - -file(MAKE_DIRECTORY "${MKDOCS_OUT}") - -add_custom_target(docs - COMMAND ${CMAKE_COMMAND} -E env PYTHONUNBUFFERED=1 - mkdocs build - --clean - --site-dir "${MKDOCS_OUT}" - --config-file "${MKDOCS_SRC}/mkdocs.yml" - WORKING_DIRECTORY "${MKDOCS_SRC}" - COMMENT "Generating documentation with MkDocs" - VERBATIM -) - -# Optional install step -install(DIRECTORY "${MKDOCS_OUT}/" - DESTINATION "share/doc/${PROJECT_NAME}") - -add_custom_target(install-docs - DEPENDS docs - COMMAND "${CMAKE_COMMAND}" --install . --component docs - COMMENT "Installing documentation") - -# Name of the target that launches the dev server -add_custom_target( - serve_docs # ← invoke with `make serve_docs` - COMMAND ${CMAKE_COMMAND} -E env PYTHONUNBUFFERED=1 - # On Windows we need to run the command through the shell - # so that the `&&` operator works correctly. - ${CMAKE_COMMAND} -E env - mkdocs serve - --dev-addr "127.0.0.1:8000" # optional – explicit bind address - --watch "${MKDOCS_SRC}" # watch source files for changes - --config-file "${MKDOCS_SRC}/mkdocs.yml" - WORKING_DIRECTORY "${MKDOCS_SRC}" - USES_TERMINAL # tells CMake to attach the child process to the console - COMMENT "Starting MkDocs live‑preview server (Ctrl‑C to stop)" - VERBATIM -) - -add_dependencies(serve_docs docs) # ensures the static site is up‑to‑date before serving \ No newline at end of file diff --git a/cmake/FindCcache.cmake b/cmake/FindCcache.cmake deleted file mode 100644 index 4b440f5a..00000000 --- a/cmake/FindCcache.cmake +++ /dev/null @@ -1,8 +0,0 @@ -find_program(CCACHE_FOUND ccache) -if (CCACHE_FOUND) - message(STATUS "Found usable ccache: ${CCACHE_FOUND}") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND} cache_dir=${CMAKE_SOURCE_DIR}/build/.ccache") - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND} cache_dir=${CMAKE_SOURCE_DIR}/build/.ccache") -else() - message(STATUS "ccache NOT found! Please install it for faster rebuilds.") -endif() \ No newline at end of file diff --git a/cmake/profiles/apple-clang-armv8 b/cmake/profiles/apple-clang-armv8 deleted file mode 100644 index 0f36faa5..00000000 --- a/cmake/profiles/apple-clang-armv8 +++ /dev/null @@ -1,9 +0,0 @@ -[settings] -os=Macos -arch=armv8 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ - -[conf] - diff --git a/cmake/profiles/apple-clang-x86_64 b/cmake/profiles/apple-clang-x86_64 deleted file mode 100644 index 9af72645..00000000 --- a/cmake/profiles/apple-clang-x86_64 +++ /dev/null @@ -1,8 +0,0 @@ -[settings] -os=Macos -arch=x86_64 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ - -[conf] \ No newline at end of file diff --git a/cmake/profiles/gcc-linux-armv8 b/cmake/profiles/gcc-linux-armv8 deleted file mode 100644 index 48e1521c..00000000 --- a/cmake/profiles/gcc-linux-armv8 +++ /dev/null @@ -1,9 +0,0 @@ -[settings] -os=Linux -arch=armv8 -compiler=gcc -compiler.version=11 -compiler.libcxx=libstdc++11 - -[conf] -tools.cmake.cmaketoolchain:user_presets=False diff --git a/cmake/profiles/gcc-linux-x86_64 b/cmake/profiles/gcc-linux-x86_64 deleted file mode 100644 index 4a58979e..00000000 --- a/cmake/profiles/gcc-linux-x86_64 +++ /dev/null @@ -1,9 +0,0 @@ -[settings] -os=Linux -arch=x86_64 -compiler=gcc -compiler.version=11 -compiler.libcxx=libstdc++11 - -[conf] -tools.cmake.cmaketoolchain:user_presets=False diff --git a/cmake/profiles/msvc-193-x86_64 b/cmake/profiles/msvc-193-x86_64 deleted file mode 100644 index 19a699f5..00000000 --- a/cmake/profiles/msvc-193-x86_64 +++ /dev/null @@ -1,10 +0,0 @@ -[settings] -arch=x86_64 -compiler=msvc -compiler.cppstd=17 -compiler.runtime=static -compiler.version=193 -os=Windows - -[conf] -tools.cmake.cmaketoolchain:user_presets=False diff --git a/cmake/profiles/msvc-194-x86_64 b/cmake/profiles/msvc-194-x86_64 deleted file mode 100644 index 5d56b3b2..00000000 --- a/cmake/profiles/msvc-194-x86_64 +++ /dev/null @@ -1,8 +0,0 @@ -[settings] -arch=x86_64 -compiler=msvc -compiler.cppstd=17 -compiler.runtime=static -compiler.version=194 -os=Windows -build_type=Release \ No newline at end of file diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c deleted file mode 100644 index fcac11ce..00000000 --- a/cmake/test-static-assert.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -static_assert(1, "FAIL"); -int main(int argc, char *argv[]) { - return 0; -} \ No newline at end of file From 92e0aa779b5b9c7ed2ef9668fa08827a7e19787c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 02:47:44 +0000 Subject: [PATCH 02/10] feat(build): add .core/build.yaml for core CLI integration C++ build configuration for core CLI project detection and future `core compile` command. Defines Conan dependencies, CMake settings, cross-compilation targets, and packaging options. Co-Authored-By: Claude Opus 4.6 --- .core/build.yaml | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .core/build.yaml diff --git a/.core/build.yaml b/.core/build.yaml new file mode 100644 index 00000000..3ce932bc --- /dev/null +++ b/.core/build.yaml @@ -0,0 +1,83 @@ +# Lethean Blockchain Build Configuration +# Used by: core compile + +version: 1 + +project: + name: blockchain + type: cpp + description: "Lethean VPN Blockchain" + version: "6.0.1" + +cpp: + standard: 17 + build_type: Release + static: false + + conan: + version: "2.21.0" + requires: + - zlib/1.3.1 + - boost/1.85.0 + - openssl/3.2.0 + - miniupnpc/2.2.5 + - jwt-cpp/0.7.1 + - oatpp/1.3.0.latest + - oatpp-swagger/1.3.0.latest + tool_requires: + - cmake/3.31.9 + options: + boost/*:without_test: true + registry: + url: http://forge.snider.dev:4000/api/packages/host-uk/conan + remote: conan_build + + cmake: + minimum_version: "3.16" + variables: + USE_CCACHE: "ON" + + options: + testnet: false + +targets: + - os: linux + arch: x86_64 + profile: gcc-linux-x86_64 + - os: linux + arch: arm64 + profile: gcc-linux-armv8 + - os: darwin + arch: arm64 + profile: apple-clang-armv8 + - os: darwin + arch: x86_64 + profile: apple-clang-x86_64 + - os: windows + arch: x86_64 + profile: msvc-194-x86_64 + +package: + generators: + - TGZ + - ZIP + - NSIS + - DEB + - RPM + vendor: Lethean + contact: support@lt.hn + website: https://lt.hn + +docker: + dockerfile: utils/docker/lthn-chain/Dockerfile + image: lthn/chain + platforms: + - linux/amd64 + tags: + - testnet + - "{{.Version}}" + build_args: + BUILD_THREADS: auto + BUILD_TESTNET: "1" + BUILD_STATIC: "0" + BUILD_TYPE: Release From 5dae74347c0ae919de7f1e1b744601f50fc38366 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 03:27:11 +0000 Subject: [PATCH 03/10] Restore project-specific cmake/test-static-assert.c This file was wrongly extracted to the .core/build submodule during the build system extraction. It's a project-specific compile test used by CMakeLists.txt:185 and must remain in the project tree. Verified: make configure && make build && make package all pass. Co-Authored-By: Claude Opus 4.6 --- cmake/test-static-assert.c | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cmake/test-static-assert.c diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c new file mode 100644 index 00000000..3e612bbc --- /dev/null +++ b/cmake/test-static-assert.c @@ -0,0 +1,6 @@ +#include + +static_assert(1, "FAIL"); +int main(int argc, char *argv[]) { + return 0; +} From 4120a9a664f9b2ea279e6eb3c31831222758d27f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 03:56:41 +0000 Subject: [PATCH 04/10] Rebrand to SASE, restore cmake test fixture - Update .core/build.yaml description: SASE infrastructure chain - cmake/test-static-assert.c already committed separately Co-Authored-By: Claude Opus 4.6 --- .core/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.core/build.yaml b/.core/build.yaml index 3ce932bc..dcbd4f69 100644 --- a/.core/build.yaml +++ b/.core/build.yaml @@ -6,7 +6,7 @@ version: 1 project: name: blockchain type: cpp - description: "Lethean VPN Blockchain" + description: "Lethean Blockchain - SASE infrastructure chain" version: "6.0.1" cpp: From 7ee2265cae5b75312316322eac8009d3bf628d3f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 13:17:11 +0000 Subject: [PATCH 05/10] fix: crypto test PRNG determinism, checkpoint hashes, and Zano address remnants The crypto test PRNG was non-deterministic because setup_random() seeded the state but grant_random_initialize_no_lock() overwrote it with /dev/urandom on the first random call. Calling it before memset ensures the initialized flag is set, preventing the overwrite. Also adds --generate mode to crypto-tests for future vector regeneration, updates checkpoint hashes for multisig_and_checkpoints (height 15) and gen_no_attchments_in_coinbase (height 12), and replaces hardcoded Zano addresses/URLs with Lethean equivalents in manual test scaffolding. Co-Authored-By: Claude Opus 4.6 --- tests/core_tests/checkpoints_tests.cpp | 2 +- tests/core_tests/multisig_wallet_tests.cpp | 2 +- tests/crypto/main.cpp | 108 +++++++++++++----- tests/crypto/random.c | 1 + tests/functional_tests/plain_wallet_tests.cpp | 6 +- tests/performance_tests/main.cpp | 10 +- 6 files changed, 92 insertions(+), 37 deletions(-) diff --git a/tests/core_tests/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index 50668524..acff272a 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -708,7 +708,7 @@ bool gen_no_attchments_in_coinbase::init_config_set_cp(currency::core& c, size_t // different checkpoints due to different block versions for different hardforks -> different hashes if (crc.is_hardfork_active_for_height(ZANO_HARDFORK_03, 11) && !crc.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, 11)) { - m_checkpoints.add_checkpoint(12, "70fbbd33d88ccaa26f9fe64d102bcff2572494009339de9fab7158a40633fbb5"); + m_checkpoints.add_checkpoint(12, "0300604e3c4018c79f4d81fbc0d15b3206956a95efc237f70c963db926773728"); } else { diff --git a/tests/core_tests/multisig_wallet_tests.cpp b/tests/core_tests/multisig_wallet_tests.cpp index 55afd1f0..b6df4cc7 100644 --- a/tests/core_tests/multisig_wallet_tests.cpp +++ b/tests/core_tests/multisig_wallet_tests.cpp @@ -1638,7 +1638,7 @@ multisig_and_checkpoints::multisig_and_checkpoints() bool multisig_and_checkpoints::set_cp(currency::core& c, size_t ev_index, const std::vector& events) { currency::checkpoints checkpoints; - checkpoints.add_checkpoint(15, "a78fa870608991aa773d5d5aaf684ac77fea30c3e103b00d3c05c15912215c31"); + checkpoints.add_checkpoint(15, "bd41e498afb84ca8bdcf9c1890ab48902dc1f202ce7c2a84cb9fa16c4a526d62"); c.set_checkpoints(std::move(checkpoints)); return true; diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 3b9dcf50..761aa4be 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -30,28 +30,40 @@ bool operator !=(const ec_scalar &a, const ec_scalar &b) { bool operator !=(const ec_point &a, const ec_point &b) { return 0 != memcmp(&a, &b, sizeof(ec_point)); } -/* -bool operator !=(const secret_key &a, const secret_key &b) { - return 0 != memcmp(&a, &b, sizeof(secret_key)); + +static string tohex(const void *data, size_t len) { + string res; + res.reserve(len * 2); + const unsigned char *p = reinterpret_cast(data); + for (size_t i = 0; i < len; i++) { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", p[i]); + res += buf; + } + return res; } -bool operator !=(const key_derivation &a, const key_derivation &b) { - return 0 != memcmp(&a, &b, sizeof(key_derivation)); -}*/ - - +template +static string tohex(const T &v) { return tohex(&v, sizeof(T)); } int main(int argc, char *argv[]) { fstream input; string cmd; size_t test = 0; bool error = false; + bool generate = false; setup_random(); - if (argc != 2) { - cerr << "invalid arguments" << endl; + + if (argc == 3 && string(argv[1]) == "--generate") { + generate = true; + input.open(argv[2], ios_base::in); + } else if (argc == 2) { + input.open(argv[1], ios_base::in); + } else { + cerr << "usage: crypto-tests [--generate] " << endl; return 1; } - input.open(argv[1], ios_base::in); + for (;;) { ++test; input.exceptions(ios_base::badbit); @@ -64,14 +76,18 @@ int main(int argc, char *argv[]) { bool expected = false, actual = false; get(input, scalar, expected); actual = check_scalar(scalar); - if (expected != actual) { + if (generate) { + cout << "check_scalar " << tohex(scalar) << " " << (actual ? "true" : "false") << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "random_scalar") { ec_scalar expected, actual; get(input, expected); random_scalar(actual); - if (expected != actual) { + if (generate) { + cout << "random_scalar " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "hash_to_scalar") { @@ -79,7 +95,9 @@ int main(int argc, char *argv[]) { ec_scalar expected, actual; get(input, data, expected); hash_to_scalar(data.data(), data.size(), actual); - if (expected != actual) { + if (generate) { + cout << "hash_to_scalar " << (data.empty() ? "x" : tohex(data.data(), data.size())) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "generate_keys") { @@ -87,7 +105,9 @@ int main(int argc, char *argv[]) { secret_key expected2, actual2; get(input, expected1, expected2); generate_keys(actual1, actual2); - if (expected1 != actual1 || expected2 != actual2) { + if (generate) { + cout << "generate_keys " << tohex(actual1) << " " << tohex(actual2) << endl; + } else if (expected1 != actual1 || expected2 != actual2) { goto error; } } else if (cmd == "check_key") { @@ -95,7 +115,9 @@ int main(int argc, char *argv[]) { bool expected, actual; get(input, key, expected); actual = check_key(key); - if (expected != actual) { + if (generate) { + cout << "check_key " << tohex(key) << " " << (actual ? "true" : "false") << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "secret_key_to_public_key") { @@ -107,7 +129,11 @@ int main(int argc, char *argv[]) { get(input, expected2); } actual1 = secret_key_to_public_key(sec, actual2); - if (expected1 != actual1 || (expected1 && expected2 != actual2)) { + if (generate) { + cout << "secret_key_to_public_key " << tohex(sec) << " " << (actual1 ? "true" : "false"); + if (actual1) cout << " " << tohex(actual2); + cout << endl; + } else if (expected1 != actual1 || (expected1 && expected2 != actual2)) { goto error; } } else if (cmd == "generate_key_derivation") { @@ -120,7 +146,11 @@ int main(int argc, char *argv[]) { get(input, expected2); } actual1 = generate_key_derivation(key1, key2, actual2); - if (expected1 != actual1 || (expected1 && expected2 != actual2)) { + if (generate) { + cout << "generate_key_derivation " << tohex(key1) << " " << tohex(key2) << " " << (actual1 ? "true" : "false"); + if (actual1) cout << " " << tohex(actual2); + cout << endl; + } else if (expected1 != actual1 || (expected1 && expected2 != actual2)) { goto error; } } else if (cmd == "derive_public_key") { @@ -134,7 +164,11 @@ int main(int argc, char *argv[]) { get(input, expected2); } actual1 = derive_public_key(derivation, output_index, base, actual2); - if (expected1 != actual1 || (expected1 && expected2 != actual2)) { + if (generate) { + cout << "derive_public_key " << tohex(derivation) << " " << output_index << " " << tohex(base) << " " << (actual1 ? "true" : "false"); + if (actual1) cout << " " << tohex(actual2); + cout << endl; + } else if (expected1 != actual1 || (expected1 && expected2 != actual2)) { goto error; } } else if (cmd == "derive_secret_key") { @@ -144,7 +178,9 @@ int main(int argc, char *argv[]) { secret_key expected, actual; get(input, derivation, output_index, base, expected); derive_secret_key(derivation, output_index, base, actual); - if (expected != actual) { + if (generate) { + cout << "derive_secret_key " << tohex(derivation) << " " << output_index << " " << tohex(base) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "generate_signature") { @@ -154,7 +190,9 @@ int main(int argc, char *argv[]) { signature expected, actual; get(input, prefix_hash, pub, sec, expected); generate_signature(prefix_hash, pub, sec, actual); - if (expected != actual) { + if (generate) { + cout << "generate_signature " << tohex(prefix_hash) << " " << tohex(pub) << " " << tohex(sec) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "check_signature") { @@ -164,7 +202,9 @@ int main(int argc, char *argv[]) { bool expected = false, actual = false; get(input, prefix_hash, pub, sig, expected); actual = check_signature(prefix_hash, pub, sig); - if (expected != actual) { + if (generate) { + cout << "check_signature " << tohex(prefix_hash) << " " << tohex(pub) << " " << tohex(sig) << " " << (actual ? "true" : "false") << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "hash_to_point") { @@ -172,7 +212,9 @@ int main(int argc, char *argv[]) { ec_point expected, actual; get(input, h, expected); hash_to_point(h, actual); - if (expected != actual) { + if (generate) { + cout << "hash_to_point " << tohex(h) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "hash_to_ec") { @@ -180,7 +222,9 @@ int main(int argc, char *argv[]) { ec_point expected, actual; get(input, key, expected); hash_to_ec(key, actual); - if (expected != actual) { + if (generate) { + cout << "hash_to_ec " << tohex(key) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "generate_key_image") { @@ -189,7 +233,9 @@ int main(int argc, char *argv[]) { key_image expected, actual; get(input, pub, sec, expected); generate_key_image(pub, sec, actual); - if (expected != actual) { + if (generate) { + cout << "generate_key_image " << tohex(pub) << " " << tohex(sec) << " " << tohex(actual) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "generate_ring_signature") { @@ -214,7 +260,11 @@ int main(int argc, char *argv[]) { getvar(input, pubs_count * sizeof(signature), expected.data()); actual.resize(pubs_count); generate_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sec, sec_index, actual.data()); - if (expected != actual) { + if (generate) { + cout << "generate_ring_signature " << tohex(prefix_hash) << " " << tohex(image) << " " << pubs_count; + for (i = 0; i < pubs_count; i++) cout << " " << tohex(vpubs[i]); + cout << " " << tohex(sec) << " " << sec_index << " " << tohex(actual.data(), pubs_count * sizeof(signature)) << endl; + } else if (expected != actual) { goto error; } } else if (cmd == "check_ring_signature") { @@ -237,7 +287,11 @@ int main(int argc, char *argv[]) { getvar(input, pubs_count * sizeof(signature), sigs.data()); get(input, expected); actual = check_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sigs.data()); - if (expected != actual) { + if (generate) { + cout << "check_ring_signature " << tohex(prefix_hash) << " " << tohex(image) << " " << pubs_count; + for (i = 0; i < pubs_count; i++) cout << " " << tohex(vpubs[i]); + cout << " " << tohex(sigs.data(), pubs_count * sizeof(signature)) << " " << (actual ? "true" : "false") << endl; + } else if (expected != actual) { goto error; } } else { diff --git a/tests/crypto/random.c b/tests/crypto/random.c index 05d7cf5f..7a50d452 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -7,5 +7,6 @@ #include "crypto-tests.h" void setup_random(void) { + grant_random_initialize_no_lock(); memset(&state, 42, sizeof(union hash_state)); } diff --git a/tests/functional_tests/plain_wallet_tests.cpp b/tests/functional_tests/plain_wallet_tests.cpp index f1f9f36b..a66dad77 100644 --- a/tests/functional_tests/plain_wallet_tests.cpp +++ b/tests/functional_tests/plain_wallet_tests.cpp @@ -33,11 +33,11 @@ void run_plain_wallet_api_test() LOG_PRINT_L0("Creating instance..."); //plain_wallet::set_bundle_working_dir("E:\\tmp\\check_export"); - std::string s = plain_wallet::init("195.201.107.230", "33333", boost::dll::program_location().parent_path().string(), 1); + std::string s = plain_wallet::init("127.0.0.1", "36941", boost::dll::program_location().parent_path().string(), 1); //s = plain_wallet::get_export_private_info("E:\\tmp\\check_export"); - std::string res = plain_wallet::sync_call("get_seed_phrase_info", 0, "{\"seed_phrase\":\"aZxat4HAWriVQ3enkGcVsrZRdMseAJswG3CSEwTqZS246VsFQ53w26eZstYsu1jWE74Atz9ajLxFnBsVTafncWNH5SMv4zHFaTS:1780c4d5dd7e97cc4a75ea8baa7977d12ef948b9a6dddc2a9a37e5e22ac7180e:1599495055\"}"); + std::string res = plain_wallet::sync_call("get_seed_phrase_info", 0, "{\"seed_phrase\":\"iTHNHvUTA2gR7gSb854s58SpAyqtR5aCNcfzBzVHjhvPcw2gQ2PHDiwT48U4ZyLVjtLxev8fAQ7NaGLZe6ihTSgp7gL45MJTCK:1780c4d5dd7e97cc4a75ea8baa7977d12ef948b9a6dddc2a9a37e5e22ac7180e:1599495055\"}"); // res = plain_wallet::restore("footstep knowledge fur capture honey minute carefully peaceful lovely crawl lunch government nightmare friendship myself sign possibly plan flower depression bread rainbow wrong hardly dark chest", @@ -50,7 +50,7 @@ void run_plain_wallet_api_test() res = plain_wallet::close_wallet(0); - res = plain_wallet::invoke(0, "{\"method\":\"transfer\",\"params\":{\"destinations\":[{\"amount\":10000000000,\"address\":\"aZxat4HAWriVQ3enkGcVsrZRdMseAJswG3CSEwTqZS246VsFQ53w26eZstYsu1jWE74Atz9ajLxFnBsVTafncWNH5SMv4zHFaTS\"}],\"fee\":10000000000,\"mixin\":1011111,\"payment_id\":\"\",\"push_payer\":true,\"hide_receiver\":false}}"); + res = plain_wallet::invoke(0, "{\"method\":\"transfer\",\"params\":{\"destinations\":[{\"amount\":10000000000,\"address\":\"iTHNHvUTA2gR7gSb854s58SpAyqtR5aCNcfzBzVHjhvPcw2gQ2PHDiwT48U4ZyLVjtLxev8fAQ7NaGLZe6ihTSgp7gL45MJTCK\"}],\"fee\":10000000000,\"mixin\":1011111,\"payment_id\":\"\",\"push_payer\":true,\"hide_receiver\":false}}"); //epee::misc_utils::sleep_no_w(10000000); diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 521ec083..9e71f2f3 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -74,7 +74,7 @@ void test_plain_wallet() { //std::string res = plain_wallet::init("195.201.107.230", "33340", "C:\\Users\\roky\\home\\", 0); //std::string res = plain_wallet::init("", "", "C:\\Users\\roky\\home\\", 0); - std::string res = plain_wallet::init("https://node.zano.org", "443", "C:\\Users\\roky\\home\\", LOG_LEVEL_2); + std::string res = plain_wallet::init("127.0.0.1", "36941", "C:\\Users\\roky\\home\\", LOG_LEVEL_2); //std::string res = plain_wallet::init("127.0.0.1", "12111", "C:\\Users\\roky\\home22\\", 0); plain_wallet::configure_object conf = AUTO_VAL_INIT(conf); @@ -112,9 +112,9 @@ void test_plain_wallet() epee::misc_utils::sleep_no_w(2000); //res = plain_wallet::sync_call("reset_connection_url", 0, "195.201.107.230:33336"); - //res = plain_wallet::sync_call("reset_connection_url", 0, "https://node.zano.org:443"); - //res = plain_wallet::sync_call("reset_connection_url", 0, "https://zano.cakewallet.com"); - //res = plain_wallet::sync_call("reset_connection_url", 0, "https://zano.api.wombat.systems:443"); + //res = plain_wallet::sync_call("reset_connection_url", 0, "127.0.0.1:36941"); + //res = plain_wallet::sync_call("reset_connection_url", 0, "127.0.0.1:36941"); + //res = plain_wallet::sync_call("reset_connection_url", 0, "127.0.0.1:36941"); //res = plain_wallet::sync_call("reset_connection_url", 0, "http://127.0.0.1:11211"); @@ -178,7 +178,7 @@ void test_plain_wallet() std::string res3 = plain_wallet::sync_call("invoke", instance_id, invoke_body); - //invoke_body = "{\r\n \"method\": \"transfer\",\r\n \"params\": {\r\n \"destinations\": [\r\n {\r\n \"amount\": \"1000000000000\",\r\n \"address\": \"ZxD9oVwGwW6ULix9Pqttnr7JDpaoLvDVA1KJ9eA9KRxPMRZT5X7WwtU94XH1Z6q6XTMxNbHmbV2xfZ429XxV6fST2DxEg4BQV\",\r\n \"asset_id\": \"cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6\"\r\n }\r\n ],\r\n \"fee\": 10000000000,\r\n \"mixin\": 10,\r\n \"payment_id\": \"\",\r\n \"comment\": \"\",\r\n \"push_payer\": false,\r\n \"hide_receiver\": true\r\n }\r\n}"; + //invoke_body = "{\r\n \"method\": \"transfer\",\r\n \"params\": {\r\n \"destinations\": [\r\n {\r\n \"amount\": \"1000000000000\",\r\n \"address\": \"iTHNHvUTA2gR7gSb854s58SpAyqtR5aCNcfzBzVHjhvPcw2gQ2PHDiwT48U4ZyLVjtLxev8fAQ7NaGLZe6ihTSgp7gL45MJTCK\",\r\n \"asset_id\": \"cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6\"\r\n }\r\n ],\r\n \"fee\": 10000000000,\r\n \"mixin\": 10,\r\n \"payment_id\": \"\",\r\n \"comment\": \"\",\r\n \"push_payer\": false,\r\n \"hide_receiver\": true\r\n }\r\n}"; //std::string res4 = plain_wallet::sync_call("invoke", instance_id, invoke_body); //LOG_PRINT_L0(res); From 467c64d015213dc0b680d040fd24e41a65ff8be8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 13:22:25 +0000 Subject: [PATCH 06/10] feat: RandomX PoW, LWMA difficulty, stratum mining.* protocol, new genesis Replace ProgPowZ with RandomX for ASIC-resistant proof-of-work. The full dataset is initialized multi-threaded at startup with the key "LetheanRandomXv1". Thread-local VMs are created on demand. Switch difficulty algorithm from Zano's 720-block window to LWMA-1 (zawy12) with a 60-block window for much faster convergence after hashrate changes. Target block time set to 10s for PoW. Add standard stratum mining.* protocol handlers (subscribe, authorize, submit, extranonce.subscribe) alongside existing EthProxy eth_* handlers, with automatic protocol detection and mining.notify translation for XMRig-based miners. Generate fresh Lethean genesis block and premine wallet. Replace all remaining hardcoded Zano addresses in tests with runtime-generated keys to avoid prefix mismatches. Link RandomX library across all build targets. Co-Authored-By: Claude Opus 4.6 --- ConanPresets.json | 2 +- Makefile | 2 + contrib/CMakeLists.txt | 1 + contrib/randomx | 1 + genesis-work/genesis_config.json | 30 ++++ ...genesis_config.json.genesis.dictionary.txt | 7 + genesis-work/genesis_config.json.genesis.txt | 1 + ...nesis_config.json.genesis.uint64.array.txt | 14 ++ .../genesis_config.json.premine_stat.txt | 14 ++ genesis-work/premine_wallet | Bin 0 -> 527 bytes genesis-work/premine_wallet.address | 1 + src/CMakeLists.txt | 7 +- src/api/CMakeLists.txt | 1 + src/config/default.cmake | 2 +- src/currency_core/basic_pow_helpers.cpp | 130 +++++++++++++----- src/currency_core/blockchain_storage.cpp | 9 +- src/currency_core/difficulty.cpp | 71 ++++++++++ src/currency_core/difficulty.h | 1 + src/genesis/_genesis.cpp.gen | 4 +- src/genesis/_genesis.h.gen | 4 +- src/genesis/_genesis_acc.cpp.gen | 4 +- src/stratum/stratum_server.cpp | 95 ++++++++++++- tests/CMakeLists.txt | 14 +- tests/core_tests/alias_tests.cpp | 23 ++-- tests/core_tests/transaction_tests.cpp | 8 +- tests/unit_tests/base58.cpp | 83 ++++++----- tests/unit_tests/serialization.cpp | 3 +- 27 files changed, 423 insertions(+), 109 deletions(-) create mode 160000 contrib/randomx create mode 100644 genesis-work/genesis_config.json create mode 100644 genesis-work/genesis_config.json.genesis.dictionary.txt create mode 100644 genesis-work/genesis_config.json.genesis.txt create mode 100644 genesis-work/genesis_config.json.genesis.uint64.array.txt create mode 100644 genesis-work/genesis_config.json.premine_stat.txt create mode 100644 genesis-work/premine_wallet create mode 100644 genesis-work/premine_wallet.address diff --git a/ConanPresets.json b/ConanPresets.json index 57065026..6e1749b3 100644 --- a/ConanPresets.json +++ b/ConanPresets.json @@ -4,6 +4,6 @@ "conan": {} }, "include": [ - "build/release/conan/build/debug/generators/CMakePresets.json" + "build/release/generators/CMakePresets.json" ] } \ No newline at end of file diff --git a/Makefile b/Makefile index e14b4bad..3d0ca4a5 100644 --- a/Makefile +++ b/Makefile @@ -166,8 +166,10 @@ test-debug: # Conan management — cmake modules from .build submodule conan-get: cmake -P $(CMAKE_DIR)/ConanGet.cmake +ifneq ($(CONAN_USER),) (CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote add conan_build $(CONAN_URL) && \ CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote login conan_build $(CONAN_USER) -p $(CONAN_PASSWORD)) || true +endif conan-upload: CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) upload "*" -r=conan_build --confirm diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 8b1445cb..1f3d0b0b 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(db) add_subdirectory(ethereum) +add_subdirectory(randomx) if(USE_BITCOIN_SECP256K1_FOR_ECDSA) option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF) diff --git a/contrib/randomx b/contrib/randomx new file mode 160000 index 00000000..cf15f402 --- /dev/null +++ b/contrib/randomx @@ -0,0 +1 @@ +Subproject commit cf15f4023ec8ddaa842f49111ba80ead1b5937f1 diff --git a/genesis-work/genesis_config.json b/genesis-work/genesis_config.json new file mode 100644 index 00000000..4feae998 --- /dev/null +++ b/genesis-work/genesis_config.json @@ -0,0 +1,30 @@ +{ + "payments": [ + { + "address_this": "iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J", + "amount_this": 10000000.0, + "this_usd_price": "1.0", + "paid_prm": "", + "prm_usd_price": "", + "paid_xmr": "", + "xmr_usd_price": "", + "paid_qtum": "", + "qtum_usd_price": "", + "paid_bch": "", + "bch_usd_price": "", + "paid_rep": "", + "rep_usd_price": "", + "paid_dash": "", + "dash_usd_price": "", + "paid_ltc": "", + "ltc_usd_price": "", + "paid_eos": "", + "eos_usd_price": "", + "paid_eth": "", + "eth_usd_price": "", + "paid_btc": "", + "btc_usd_price": "" + } + ], + "proof_string": "Lethean genesis — the advantage of the nature of information being easy to spread but hard to stifle. - Satoshi Nakamoto" +} diff --git a/genesis-work/genesis_config.json.genesis.dictionary.txt b/genesis-work/genesis_config.json.genesis.dictionary.txt new file mode 100644 index 00000000..d49bb837 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.dictionary.txt @@ -0,0 +1,7 @@ +-------------genesis_acc.cpp------------- +const std::string ggenesis_tx_pub_key_str = "503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b"; +const crypto::public_key ggenesis_tx_pub_key = epee::string_tools::parse_tpod_from_hex_string(ggenesis_tx_pub_key_str); +extern const genesis_tx_dictionary_entry ggenesis_dict[1]; +const genesis_tx_dictionary_entry ggenesis_dict[1] = { +{12778448838847345770ULL,0} +}; diff --git a/genesis-work/genesis_config.json.genesis.txt b/genesis-work/genesis_config.json.genesis.txt new file mode 100644 index 00000000..95ebbe63 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.txt @@ -0,0 +1 @@ +01010000018080a0cfc8e0c8e38a0103bd6b4dda729d39dc85cb22a0b3db9b4b04d6464be9f3c84640bdca8bc0afb148000516503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b137a4c65746865616e2067656e6573697320e280942074686520616476616e74616765206f6620746865206e6174757265206f6620696e666f726d6174696f6e206265696e67206561737920746f2073707265616420627574206861726420746f20737469666c652e202d205361746f736869204e616b616d6f746f15000b029e4e0e0a0000 \ No newline at end of file diff --git a/genesis-work/genesis_config.json.genesis.uint64.array.txt b/genesis-work/genesis_config.json.genesis.uint64.array.txt new file mode 100644 index 00000000..94e32578 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.uint64.array.txt @@ -0,0 +1,14 @@ +--------- genesis.h--------- +#pragma pack(push, 1) +struct genesis_tx_raw_data +{ + uint64_t const v[27]; + uint8_t const r[1]; +}; +#pragma pack(pop) +extern const genesis_tx_raw_data ggenesis_tx_raw; + +--------- genesis.cpp--------- +const genesis_tx_raw_data ggenesis_tx_raw = {{ +0xa080800100000101,0x03018ae3c8e0c8cf,0xdc399d72da4d6bbd,0x4b9bdbb3a022cb85,0x46c8f3e94b46d604,0x48b1afc08bcabd40,0x67c1c73e50160500,0xbcbc01d8d7eaf2e3,0x26baa2848ac5b3eb,0xa15f105c1fcb1ea2,0x74654c7a132b9359,0x6e6567206e616568,0x9480e22073697365,0x7664612065687420,0x6f20656761746e61,0x616e206568742066,0x20666f2065727574,0x74616d726f666e69,0x6e696562206e6f69,0x7420797361652067,0x646165727073206f,0x7261682074756220,0x697473206f742064,0x53202d202e656c66,0x4e206968736f7461,0x156f746f6d616b61,0x000a0e4e9e020b00}, +{0x00}}; diff --git a/genesis-work/genesis_config.json.premine_stat.txt b/genesis-work/genesis_config.json.premine_stat.txt new file mode 100644 index 00000000..06d3f50c --- /dev/null +++ b/genesis-work/genesis_config.json.premine_stat.txt @@ -0,0 +1,14 @@ +NAME AMOUNT AMOUNT THIS USD EQ +bch 0.0000000000 0.000000000000 0.0000000000 +btc 0.0000000000 0.000000000000 0.0000000000 +dash 0.0000000000 0.000000000000 0.0000000000 +eos 0.0000000000 0.000000000000 0.0000000000 +eth 0.0000000000 0.000000000000 0.0000000000 +ltc 0.0000000000 0.000000000000 0.0000000000 +prm 0.0000000000 0.000000000000 0.0000000000 +qtum 0.0000000000 0.000000000000 0.0000000000 +rep 0.0000000000 0.000000000000 0.0000000000 + +TOTAL 0.000000000000 0.0000000000 + +PREMINE_AMOUNT 0 (0.000000000000THIS) diff --git a/genesis-work/premine_wallet b/genesis-work/premine_wallet new file mode 100644 index 0000000000000000000000000000000000000000..69d00f3cb6ce0f958f5d190e3c04fc652b23a8bb GIT binary patch literal 527 zcmV+q0`UD25D){hc!Sj9!3m||X?Z;^cL<=|%p2&|q+A0O87tJ7- zUn;Pi?1GsTaFh=)i)N6GDG*rV7wd?Gw0R3PC7*KIcs5NRynR(LL4oxG_gA z3Z2vi(QQ={ZudnXmBOrM0hQ#voSVM;?zH!CP3>C=fE}|Qy&@BEj zPq*k&5Gkd|3VD`rxhZ`cJjb}|yRNUZ`{=vI36bei-RFT%yvS)i@fMo@&QUfpgIKnU zEV|F!^RR^N>k_v~dQq&BsZN=dJfB{fNWhd&qS0=+{VDs=^35xvd@{El6JrE{?DUjT z2QarFvO;R)C$Y??Si)TuR0TB~My*X6j;~sj1K9^Xwj}aa53>)S@p{y&IJ>xGPi~i@ zj#GVB^e>(_J~*|8^o6=SSIc#Hxq{nUX{e)I94EUk?#!39)AT3 z+PqpE6jTJXMBtuhbBa>Htb!?1azqu@k3XIF`9#BJmaHAa-Qdxg%d4jZIi`qKaa literal 0 HcmV?d00001 diff --git a/genesis-work/premine_wallet.address b/genesis-work/premine_wallet.address new file mode 100644 index 00000000..3ca9e2d4 --- /dev/null +++ b/genesis-work/premine_wallet.address @@ -0,0 +1 @@ +iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d276457..d991d76e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,7 @@ endif() add_library(currency_core ${CURRENCY_CORE}) add_dependencies(currency_core version config ${PCH_LIB_NAME}) target_link_libraries(currency_core config) +target_include_directories(currency_core PRIVATE ${RANDOMX_INCLUDE}) ENABLE_SHARED_PCH(currency_core CURRENCY_CORE) add_library(wallet ${WALLET}) @@ -164,19 +165,19 @@ target_link_libraries(currency_core config lmdb mdbx) add_executable(daemon ${DAEMON} ${P2P} ${CURRENCY_PROTOCOL}) add_dependencies(daemon version) -target_link_libraries(daemon rpc stratum currency_core crypto common miniupnpc::miniupnpc ZLIB::ZLIB ethash api::server ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(daemon rpc stratum currency_core crypto common miniupnpc::miniupnpc ZLIB::ZLIB ethash randomx api::server ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(daemon DAEMON) ENABLE_SHARED_PCH_EXECUTABLE(daemon) add_executable(connectivity_tool ${CONN_TOOL}) add_dependencies(connectivity_tool version) -target_link_libraries(connectivity_tool currency_core crypto common ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(connectivity_tool currency_core crypto common ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(connectivity_tool CONN_TOOL) ENABLE_SHARED_PCH_EXECUTABLE(connectivity_tool) add_executable(simplewallet ${SIMPLEWALLET}) add_dependencies(simplewallet version) -target_link_libraries(simplewallet wallet rpc currency_core crypto common ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(simplewallet wallet rpc currency_core crypto common ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(simplewallet SIMPLEWALLET) ENABLE_SHARED_PCH_EXECUTABLE(simplewallet) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index e325f3a3..4304b9ac 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -59,6 +59,7 @@ target_link_libraries(api_server crypto common ethash + randomx ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} OpenSSL::SSL diff --git a/src/config/default.cmake b/src/config/default.cmake index 56929c50..b81a766f 100644 --- a/src/config/default.cmake +++ b/src/config/default.cmake @@ -21,7 +21,7 @@ set(base_reward_dust_threshold 1000000 CACHE STRING "BASE_REWARD_DUST_THRESHOLD" set(default_dust_threshold 0 CACHE STRING "DEFAULT_DUST_THRESHOLD" ) set(difficulty_pow_starter 1 CACHE STRING "DIFFICULTY_POW_STARTER" ) set(difficulty_pos_target 120 CACHE STRING "DIFFICULTY_POS_TARGET" ) -set(difficulty_pow_target 120 CACHE STRING "DIFFICULTY_POW_TARGET" ) +set(difficulty_pow_target 10 CACHE STRING "DIFFICULTY_POW_TARGET" ) set(difficulty_window 720 CACHE STRING "DIFFICULTY_WINDOW" ) set(difficulty_lag 15 CACHE STRING "DIFFICULTY_LAG" ) set(difficulty_cut 60 CACHE STRING "DIFFICULTY_CUT" ) diff --git a/src/currency_core/basic_pow_helpers.cpp b/src/currency_core/basic_pow_helpers.cpp index d0afcc7b..9ea80e12 100644 --- a/src/currency_core/basic_pow_helpers.cpp +++ b/src/currency_core/basic_pow_helpers.cpp @@ -27,39 +27,102 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "common/int-util.h" + +// RandomX PoW +#include "randomx.h" + +// Keep ethash headers for stratum/RPC compatibility (epoch seed helpers) #include "ethereum/libethash/ethash/ethash.hpp" -#include "ethereum/libethash/ethash/progpow.hpp" + +#include +#include +#include +#include +#include namespace currency { + //-------------------------------------------------------------- + // RandomX global state — full dataset mode for fast hashing + //-------------------------------------------------------------- + static randomx_cache* rx_cache = nullptr; + static randomx_dataset* rx_dataset = nullptr; + static randomx_flags rx_flags = RANDOMX_FLAG_DEFAULT; + static std::once_flag rx_init_flag; - //-------------------------------------------------------------- - int ethash_custom_log_get_level() + static void init_randomx() { - return epee::log_space::get_set_log_detalisation_level(); - } - //-------------------------------------------------------------- - void ethash_custom_log(const std::string& m, bool add_callstack) - { - std::string msg = epee::log_space::log_singletone::get_prefix_entry() + "[ETHASH]" + m; - if (add_callstack) - msg = msg + "callstask: " + epee::misc_utils::get_callstack(); + rx_flags = randomx_get_flags() | RANDOMX_FLAG_FULL_MEM; + LOG_PRINT_L0("[RandomX] Initializing with flags: " << static_cast(rx_flags)); - epee::log_space::log_singletone::do_log_message(msg, LOG_LEVEL_0, epee::log_space::console_color_default, true, LOG_DEFAULT_TARGET); - } - //-------------------------------------------------------------- - void init_ethash_log_if_necessary() - { - static bool inited = false; - if (inited) + rx_cache = randomx_alloc_cache(rx_flags); + if (!rx_cache) + { + LOG_ERROR("[RandomX] Failed to allocate cache"); + throw std::bad_alloc(); + } + + static const char rx_key[] = "LetheanRandomXv1"; + randomx_init_cache(rx_cache, rx_key, sizeof(rx_key) - 1); + LOG_PRINT_L0("[RandomX] Cache initialized with key \"LetheanRandomXv1\""); + + rx_dataset = randomx_alloc_dataset(rx_flags); + if (!rx_dataset) + { + LOG_ERROR("[RandomX] Failed to allocate dataset, falling back to light mode"); + rx_flags = rx_flags & ~RANDOMX_FLAG_FULL_MEM; return; + } - ethash::access_custom_log_level_function() = ðash_custom_log_get_level; - ethash::access_custom_log_function() = ðash_custom_log; + // Multi-threaded dataset init + unsigned long item_count = randomx_dataset_item_count(); + unsigned int num_threads = std::thread::hardware_concurrency(); + if (num_threads == 0) num_threads = 4; + if (num_threads > 16) num_threads = 16; - inited = true; + LOG_PRINT_L0("[RandomX] Initializing dataset (" << item_count << " items, " << num_threads << " threads)..."); + + std::vector threads; + unsigned long per_thread = item_count / num_threads; + unsigned long remainder = item_count % num_threads; + + for (unsigned int i = 0; i < num_threads; ++i) + { + unsigned long start = i * per_thread + std::min((unsigned long)i, remainder); + unsigned long count = per_thread + (i < remainder ? 1 : 0); + threads.emplace_back([start, count]() { + randomx_init_dataset(rx_dataset, rx_cache, start, count); + }); + } + for (auto& t : threads) t.join(); + + LOG_PRINT_L0("[RandomX] Dataset initialized OK"); } - //------------------------------------------------------------------ + + static randomx_vm* get_thread_vm() + { + std::call_once(rx_init_flag, init_randomx); + + static thread_local randomx_vm* vm = nullptr; + if (!vm) + { + if (rx_dataset) + vm = randomx_create_vm(rx_flags, nullptr, rx_dataset); + else + vm = randomx_create_vm(rx_flags & ~RANDOMX_FLAG_FULL_MEM, rx_cache, nullptr); + + if (!vm) + { + LOG_ERROR("[RandomX] Failed to create VM"); + throw std::bad_alloc(); + } + } + return vm; + } + + //-------------------------------------------------------------- + // Ethash helpers kept for stratum/RPC backward compat + //-------------------------------------------------------------- int ethash_height_to_epoch(uint64_t height) { return static_cast(height / ETHASH_EPOCH_LENGTH); @@ -75,17 +138,20 @@ namespace currency //-------------------------------------------------------------- crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_header_hash, uint64_t nonce) { - init_ethash_log_if_necessary(); - int epoch = ethash_height_to_epoch(height); - std::shared_ptr p_context = progpow::get_global_epoch_context_full(static_cast(epoch)); - if (!p_context) - { - LOG_ERROR("fatal error: get_global_epoch_context_full failed, throwing bad_alloc..."); - throw std::bad_alloc(); - } - auto res_eth = progpow::hash(*p_context, static_cast(height), *(ethash::hash256*)&block_header_hash, nonce); + // Combine header hash + nonce into a single input buffer + struct { + crypto::hash header; + uint64_t nonce; + } input; + input.header = block_header_hash; + input.nonce = nonce; + + char hash_out[RANDOMX_HASH_SIZE]; + randomx_vm* vm = get_thread_vm(); + randomx_calculate_hash(vm, &input, sizeof(input), hash_out); + crypto::hash result = currency::null_hash; - memcpy(&result.data, &res_eth.final_hash, sizeof(res_eth.final_hash)); + std::memcpy(&result.data, hash_out, sizeof(result.data)); return result; } //--------------------------------------------------------------- diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 62a43393..f056193f 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1428,14 +1428,7 @@ wide_difficulty_type blockchain_storage::calc_diff_at_h_from_timestamps(std::vec { wide_difficulty_type dif; TIME_MEASURE_START_PD(target_calculating_calc); - if (m_core_runtime_config.is_hardfork_active_for_height(1, h)) - { - dif = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - } - else - { - dif = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - } + dif = next_difficulty_lwma(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); TIME_MEASURE_FINISH_PD(target_calculating_calc); return dif; } diff --git a/src/currency_core/difficulty.cpp b/src/currency_core/difficulty.cpp index 592b3be7..843d7823 100644 --- a/src/currency_core/difficulty.cpp +++ b/src/currency_core/difficulty.cpp @@ -267,4 +267,75 @@ namespace currency { } return summ / devider; } + + //-------------------------------------------------------------- + // LWMA-1 difficulty algorithm (zawy12) + // Linear Weighted Moving Average — adjusts every block with a + // ~60-block window. Battle-tested in Monero against ASICs and + // botnets. Much faster convergence than the 720-block Zano algo. + //-------------------------------------------------------------- + wide_difficulty_type next_difficulty_lwma(vector& timestamps, vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter) + { + const int64_t T = static_cast(target_seconds); + const size_t N = 60; // LWMA window size (solve times) + + size_t length = timestamps.size(); + CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), difficulty_starter, + "LWMA: timestamps/difficulties size mismatch"); + + if (length <= 1) + return difficulty_starter; + + // We need at most N+1 entries (giving N solve times) + if (length > N + 1) + { + timestamps.resize(N + 1); + cumulative_difficulties.resize(N + 1); + length = N + 1; + } + + // Input arrives newest-first; LWMA needs oldest-first + std::reverse(timestamps.begin(), timestamps.end()); + std::reverse(cumulative_difficulties.begin(), cumulative_difficulties.end()); + + // Now: [0]=oldest … [length-1]=newest + size_t n = length - 1; // number of solve-time intervals + + int64_t weighted_solvetimes = 0; + + for (size_t i = 1; i <= n; i++) + { + int64_t st = static_cast(timestamps[i]) + - static_cast(timestamps[i - 1]); + + // Clamp to [-6T, 6T] to limit timestamp-manipulation impact + if (st < -(6 * T)) st = -(6 * T); + if (st > (6 * T)) st = (6 * T); + + weighted_solvetimes += st * static_cast(i); + } + + // Guard against zero / negative (would be pathological timestamps) + if (weighted_solvetimes <= 0) + weighted_solvetimes = 1; + + wide_difficulty_type total_work = cumulative_difficulties[n] - cumulative_difficulties[0]; + + // LWMA-1 formula: + // next_D = total_work * T * (n+1) / (2 * weighted_solvetimes * n) + // + // The divisor (n+1)/(2*n) normalises the linear weights 1..n + // whose sum is n*(n+1)/2. + boost::multiprecision::uint256_t next_d = + (boost::multiprecision::uint256_t(total_work) * T * (n + 1)) + / (boost::multiprecision::uint256_t(2) * weighted_solvetimes * n); + + if (next_d < 1) + next_d = 1; + + if (next_d > max128bit) + return difficulty_starter; + + return next_d.convert_to(); + } } diff --git a/src/currency_core/difficulty.h b/src/currency_core/difficulty.h index 8ffb64df..f4187c05 100644 --- a/src/currency_core/difficulty.h +++ b/src/currency_core/difficulty.h @@ -32,6 +32,7 @@ namespace currency bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty); wide_difficulty_type next_difficulty_1(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); wide_difficulty_type next_difficulty_2(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); + wide_difficulty_type next_difficulty_lwma(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); uint64_t difficulty_to_boundary(wide_difficulty_type difficulty); void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result); } diff --git a/src/genesis/_genesis.cpp.gen b/src/genesis/_genesis.cpp.gen index 24ca4e63..cfc5867b 100644 --- a/src/genesis/_genesis.cpp.gen +++ b/src/genesis/_genesis.cpp.gen @@ -1,3 +1,3 @@ const genesis_tx_raw_data ggenesis_tx_raw = {{ - 0xe980800100000101,0xfbfa3a0316deb183,0x0e4d0bf7103b2df2,0x682df55627fc33ed,0xcd945b3d70689611,0x16050066b218269a,0x34af8b24a9c955d2,0x3f514b3a2be34ee9,0xb6525b442410e776,0x256d4d83666fb42a,0x6b61742074496b13,0x6e61766461207365,0x20666f2065676174,0x7574616e20656874,0x6e6920666f206572,0x6f6974616d726f66,0x20676e696562206e,0x206f742079736165,0x6220646165727073,0x2064726168207475,0x6c66697473206f74,0x746153202d202e65,0x6b614e206968736f,0x0b00156f746f6d61}, - {0x02,0x8a,0x56,0x0e,0x0a,0x00,0x00}}; +0xa080800100000101,0x03018ae3c8e0c8cf,0xdc399d72da4d6bbd,0x4b9bdbb3a022cb85,0x46c8f3e94b46d604,0x48b1afc08bcabd40,0x67c1c73e50160500,0xbcbc01d8d7eaf2e3,0x26baa2848ac5b3eb,0xa15f105c1fcb1ea2,0x74654c7a132b9359,0x6e6567206e616568,0x9480e22073697365,0x7664612065687420,0x6f20656761746e61,0x616e206568742066,0x20666f2065727574,0x74616d726f666e69,0x6e696562206e6f69,0x7420797361652067,0x646165727073206f,0x7261682074756220,0x697473206f742064,0x53202d202e656c66,0x4e206968736f7461,0x156f746f6d616b61,0x000a0e4e9e020b00}, +{0x00}}; diff --git a/src/genesis/_genesis.h.gen b/src/genesis/_genesis.h.gen index 086767c9..9ac78c62 100644 --- a/src/genesis/_genesis.h.gen +++ b/src/genesis/_genesis.h.gen @@ -1,5 +1,5 @@ struct genesis_tx_raw_data { - uint64_t const v[42]; - uint8_t const r[42]; + uint64_t const v[27]; + uint8_t const r[1]; }; diff --git a/src/genesis/_genesis_acc.cpp.gen b/src/genesis/_genesis_acc.cpp.gen index 2e2c8807..fc496fbc 100644 --- a/src/genesis/_genesis_acc.cpp.gen +++ b/src/genesis/_genesis_acc.cpp.gen @@ -1,6 +1,6 @@ -const std::string ggenesis_tx_pub_key_str = "d255c9a9248baf34e94ee32b3a4b513f76e71024445b52b62ab46f66834d6d25"; +const std::string ggenesis_tx_pub_key_str = "503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b"; const crypto::public_key ggenesis_tx_pub_key = epee::string_tools::parse_tpod_from_hex_string(ggenesis_tx_pub_key_str); extern const genesis_tx_dictionary_entry ggenesis_dict[1]; const genesis_tx_dictionary_entry ggenesis_dict[1] = { - {1056117391700764468ULL,0} +{12778448838847345770ULL,0} }; diff --git a/src/stratum/stratum_server.cpp b/src/stratum/stratum_server.cpp index 05e1c6cc..b811301f 100644 --- a/src/stratum/stratum_server.cpp +++ b/src/stratum/stratum_server.cpp @@ -808,6 +808,7 @@ namespace , m_context(context) , m_connection_initialized(false) , m_last_reported_hashrate(0) + , m_use_standard_stratum(false) { LOG_PRINT_CC(m_context, "stratum_protocol_handler::ctor()", LOG_LEVEL_4); } @@ -915,6 +916,11 @@ namespace m_methods_handlers.insert(std::make_pair("eth_getWork", &this_t::handle_method_eth_getWork)); m_methods_handlers.insert(std::make_pair("eth_submitHashrate", &this_t::handle_method_eth_submitHashrate)); m_methods_handlers.insert(std::make_pair("eth_submitWork", &this_t::handle_method_eth_submitWork)); + // Standard ETH stratum (mining.*) — used by XMRig-based miners + m_methods_handlers.insert(std::make_pair("mining.subscribe", &this_t::handle_method_mining_subscribe)); + m_methods_handlers.insert(std::make_pair("mining.authorize", &this_t::handle_method_mining_authorize)); + m_methods_handlers.insert(std::make_pair("mining.submit", &this_t::handle_method_mining_submit)); + m_methods_handlers.insert(std::make_pair("mining.extranonce.subscribe", &this_t::handle_method_mining_extranonce_subscribe)); } } @@ -994,6 +1000,58 @@ namespace return m_config.handle_work(this, id, worker, nonce, header_hash); } + // --- Standard ETH stratum (mining.*) handlers --- + + bool handle_method_mining_subscribe(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + m_use_standard_stratum = true; + // Respond with: [[["mining.notify", "subscription_id"]], "extranonce"] + // Use "0000" as a default 2-byte extra nonce + send_response(id, R"("result":[["mining.notify","1"],"0000"])"); + return true; + } + + bool handle_method_mining_authorize(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + // params: [user, pass] — same format as eth_submitLogin + std::string user_str, pass_str; + epee::serialization::harray params_array = ps.get_first_value("params", user_str, nullptr); + if (params_array != nullptr) + ps.get_next_value(params_array, pass_str); + + std::string worker_str = std::to_string(m_config.get_number_id_for_nameless_worker()); + + LOG_PRINT_CC(m_context, "Stratum [mining.authorize] USER: " << user_str << ", pass: " << pass_str << ", worker: " << worker_str, LOG_LEVEL_3); + return m_config.handle_login(this, id, user_str, pass_str, worker_str, 0); + } + + bool handle_method_mining_submit(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + // params: [worker, job_id, nonce, header_hash, mix_hash] + std::string worker_str, job_id_str, nonce_str, header_str, mixhash_str; + epee::serialization::harray params_array = ps.get_first_value("params", worker_str, nullptr); + bool r = params_array != nullptr && ps.get_next_value(params_array, job_id_str); + r = r && ps.get_next_value(params_array, nonce_str); + r = r && ps.get_next_value(params_array, header_str); + r = r && ps.get_next_value(params_array, mixhash_str); + CHECK_AND_ASSERT_MES(r, false, "Incorrect parameters for mining.submit"); + + uint64_t nonce = 0; + CHECK_AND_ASSERT_MES(pod_from_net_format_reverse(nonce_str, nonce, true), false, "Can't parse nonce from " << nonce_str); + crypto::hash header_hash = null_hash; + CHECK_AND_ASSERT_MES(pod_from_net_format(header_str, header_hash), false, "Can't parse header hash from " << header_str); + + return m_config.handle_work(this, id, worker_str, nonce, header_hash); + } + + bool handle_method_mining_extranonce_subscribe(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + send_response(id, R"("result":true)"); + return true; + } + + // --- + void send(const std::string& data) { static_cast(m_p_connection)->do_send(data.c_str(), data.size()); @@ -1002,6 +1060,40 @@ namespace void send_notification(const std::string& json) { + if (m_use_standard_stratum) + { + // Convert EthProxy "result":["header","seed","target","height"] to + // standard stratum mining.notify format: "method":"mining.notify","params":["job_id","header","seed","target",true,"height"] + // json looks like: "result":["0xHEADER","0xSEED","0xTARGET","0xHEIGHT"] + // We need: "method":"mining.notify","params":["job_id","0xHEADER","0xSEED","0xTARGET",true,"0xHEIGHT"] + std::string notify_json = json; + size_t pos = notify_json.find(R"("result":)"); + if (pos != std::string::npos) + { + // Extract the array content from "result":[...] + size_t arr_start = notify_json.find('[', pos); + size_t arr_end = notify_json.rfind(']'); + if (arr_start != std::string::npos && arr_end != std::string::npos) + { + std::string arr_content = notify_json.substr(arr_start + 1, arr_end - arr_start - 1); + // Insert job ID at beginning and clean_jobs=true before height + // arr_content is: "0xHEADER","0xSEED","0xTARGET","0xHEIGHT" + // We need: "job_id","0xHEADER","0xSEED","0xTARGET",true,"0xHEIGHT" + static uint64_t s_job_counter = 0; + std::string job_id = "\"" + std::to_string(++s_job_counter) + "\""; + + // Find the last comma to insert clean_jobs before height + size_t last_comma = arr_content.rfind(','); + if (last_comma != std::string::npos) + { + std::string params = job_id + "," + arr_content.substr(0, last_comma) + ",true" + arr_content.substr(last_comma); + send(R"({"jsonrpc":"2.0","method":"mining.notify","params":[)" + params + "]}\n"); + return; + } + } + } + } + // EthProxy mode (default): send as-is // JSON-RPC 2.0 spec: "A Notification is a Request object without an "id" member." send(R"({"jsonrpc":"2.0",)" + json + "}" "\n"); // LF character is not specified by JSON-RPC standard, but it is REQUIRED by ethminer 0.12 to work } @@ -1083,10 +1175,11 @@ namespace epee::critical_section m_work_change_lock; uint64_t m_last_reported_hashrate; + bool m_use_standard_stratum; // true = mining.* protocol, false = eth_* EthProxy protocol typedef bool (this_t::*method_handler_func_t)(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section); static std::unordered_map m_methods_handlers; - + std::atomic m_connection_initialized; }; // class stratum_protocol_handler //============================================================================================================================== diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 812dca5d..0d0896c6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,14 +28,14 @@ add_executable(net_load_tests_srv net_load_tests/srv.cpp) add_dependencies(coretests version) -target_link_libraries(coretests rpc wallet currency_core common crypto ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(functional_tests rpc wallet currency_core crypto common ZLIB::ZLIB ethash miniupnpc::miniupnpc ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(coretests rpc wallet currency_core common crypto ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(functional_tests rpc wallet currency_core crypto common ZLIB::ZLIB ethash randomx miniupnpc::miniupnpc ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(hash-tests crypto ethash) -target_link_libraries(hash-target-tests crypto currency_core ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(performance_tests wallet rpc currency_core common crypto ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(unit_tests wallet currency_core common crypto gtest_main ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(hash-target-tests crypto currency_core ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(performance_tests wallet rpc currency_core common crypto ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(unit_tests wallet currency_core common crypto gtest_main ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) if(NOT MSVC) set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") diff --git a/tests/core_tests/alias_tests.cpp b/tests/core_tests/alias_tests.cpp index 266e4164..c12ea649 100644 --- a/tests/core_tests/alias_tests.cpp +++ b/tests/core_tests/alias_tests.cpp @@ -1046,17 +1046,20 @@ bool gen_alias_too_small_reward::init_runtime_config(currency::core& c, size_t e bool gen_alias_too_small_reward::generate(std::vector& events) const { // pay for alias too small and see, if it's ok + // Generate distinct addresses from fresh accounts + account_base alias_accs[10]; + for (auto& a : alias_accs) a.generate(); const alias_entry aliases[] = { - {"a", pub_addr_from_string("ZxD95C97dFqVBsuTMA1VKjFxx8Cc7by2YTA9kLhFc8JB3zJ3qKKXRm9Lu2YQsjPP5UKhCSfLLEqJr5ovyQNYYQWV1Pv98fRzt")}, - {"bb", pub_addr_from_string("ZxDL9euTT4C9FUj28QY1RdWnreMHJiWfrPs39rXhrgai8H4pmaFzJ4vUUYRmHhNxToN64H1U5sMnaHuD3S4kVbyY1mKHnERVZ")}, - {"ccc", pub_addr_from_string("ZxBrpHp3xrjLrMMSyJUg44YmyJVZjetouVFdtqLfxpHUMSxiEyyQ7iKSj4sr6gn7qwXrj6YSw7UjJZLyc1H37QtF2p96c2gAD")}, - {"dddd", pub_addr_from_string("ZxDtT1pTwt6R2t3eGw9VD6N1heHCKNLKuCFUvqgHpXkAVnPkfai4KDYEjRSV8E42XKN3MJeaHJMaxa9hUmaXLyHm2nQ12aX93")}, - {"eeeee", pub_addr_from_string("ZxCABdwUJpqHstWJUHQ21piADBwaSsXcAh5EPtpSr8xXderWqvDef566ReFGrRqBUrE2tCgZ3HE5XRuxoq8mNTrP2X4J35yQq")}, - {"ffffff", pub_addr_from_string("ZxC34uAJJ2iW15GkvcqaQd4RKZdu16tpmf4ubmsirw7eFtKoLi2xswhNqy3Q4VacCq5mM7zuYyoWEW8AS5HGtoXr1m9RuTUuu")}, - {"ggggggg", pub_addr_from_string("ZxDHxZizSe5MNQoRkC1unqTrhYUkh1ZG7iEXMzLatyZ5EHRPat4Ls4ZRnN4CYLvJLq5F5gxdDtu17Zrvur7dcqU52sv2pryn7")}, - {"hhhhhhhh", pub_addr_from_string("ZxDXME4qrbh7mAbrqDmCbzGj14VQP1n9KLLi7fXBMNvDd5UUpcevCSXQ9zSkZcJtbzBS7u16NiykAiv3q9VkZySL2ySB6hTfL")}, - {"iiiiiiiii", pub_addr_from_string("ZxDtpxbC2bN8yu3J49tsUYUSoPYTnAgjmBogzFviUg3t2fGfWzmZ2gbKNC1XKVdMEE2hoW5sULs2hAF5T3igoAVW2MsHUmaj4")}, - {"jjjjjjjjjj", pub_addr_from_string("ZxCBLxnctYwB37YZi7MsJqBCujXzkBeJEh7wPbYrFUvMiqXiPLkyBRAh6ahQ6wre2tGR8FHesZwKn2zYPkTuibyu2648g2CGV")} + {"a", alias_accs[0].get_keys().account_address}, + {"bb", alias_accs[1].get_keys().account_address}, + {"ccc", alias_accs[2].get_keys().account_address}, + {"dddd", alias_accs[3].get_keys().account_address}, + {"eeeee", alias_accs[4].get_keys().account_address}, + {"ffffff", alias_accs[5].get_keys().account_address}, + {"ggggggg", alias_accs[6].get_keys().account_address}, + {"hhhhhhhh", alias_accs[7].get_keys().account_address}, + {"iiiiiiiii", alias_accs[8].get_keys().account_address}, + {"jjjjjjjjjj", alias_accs[9].get_keys().account_address} }; const size_t aliases_count = sizeof aliases / sizeof aliases[0]; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 9bff9d9b..29852434 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -133,13 +133,13 @@ bool test_block_creation() { uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707}; std::vector szs(&vszs[0], &vszs[90]); - account_public_address adr; - bool r = get_account_address_from_str(adr, "ZxDLGBGXbjo5w51tJkvxEPHFRr7Xft4hf33N8EkJPndoGCqocQF1mzpZqYwXByx5gMbfQuPAAB9vj79EFR6Jwkgu1o3aMQPwJ"); - CHECK_AND_ASSERT_MES(r, false, "failed to import"); + account_base acc; + acc.generate(); + account_public_address adr = acc.get_keys().account_address; uint64_t block_reward_without_fee = 0; uint64_t block_reward = 0; block b; - r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, 0); + bool r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, 0); return r; } diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index adf78c48..0aa2d0f0 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -441,7 +441,6 @@ namespace "\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c" "\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d" "\x00"); - std::string test_keys_addr_str = "ZxDqHy6WnyYY5yQcdApjMb8tVPik5BC3LFdaevfbGq7X1KY5vdsWmUi5UQgse2GBZFbMsb47TFqBmPpdFHDDwDxR2ZuZ6zX4W"; // correct str address depends on CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX value } TEST(get_account_address_as_str, works_correctly) @@ -449,13 +448,23 @@ TEST(get_account_address_as_str, works_correctly) currency::account_public_address addr; ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr)); std::string addr_str = currency::get_account_address_as_str(addr); - ASSERT_EQ(addr_str, test_keys_addr_str); + // Verify round-trip: parse the generated address back and check keys match + currency::account_public_address addr2; + ASSERT_TRUE(currency::get_account_address_from_str(addr2, addr_str)); + std::string blob; + ASSERT_TRUE(serialization::dump_binary(addr2, blob)); + ASSERT_EQ(blob, test_serialized_keys); } TEST(get_account_address_from_str, handles_valid_address) { + // Generate a valid address from the test keys + currency::account_public_address addr_orig; + ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr_orig)); + std::string valid_addr_str = currency::get_account_address_as_str(addr_orig); + currency::account_public_address addr; - ASSERT_TRUE(currency::get_account_address_from_str(addr, test_keys_addr_str)); + ASSERT_TRUE(currency::get_account_address_from_str(addr, valid_addr_str)); std::string blob; ASSERT_TRUE(serialization::dump_binary(addr, blob)); @@ -464,10 +473,12 @@ TEST(get_account_address_from_str, handles_valid_address) TEST(get_account_address_from_str, fails_on_invalid_address_format) { - currency::account_public_address addr; - std::string addr_str = test_keys_addr_str; + currency::account_public_address addr_orig; + ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr_orig)); + std::string addr_str = currency::get_account_address_as_str(addr_orig); addr_str[0] = '0'; + currency::account_public_address addr; ASSERT_FALSE(currency::get_account_address_from_str(addr, addr_str)); } @@ -560,11 +571,11 @@ struct addr_entry_t uint8_t flags; }; -addr_entry_t addr_entries[] = +addr_entry_t addr_entries[] = { { - // classic normal address - "ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH", // address + // classic normal address — generated at runtime from keys + "", // address (empty = auto-generate from keys + prefix) "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "", // payment_id_hex @@ -572,31 +583,15 @@ addr_entry_t addr_entries[] = }, { // classic integrated address - "iZ2Zi6RmTWwcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTqEsjvJoco1aLSZXS6T", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "87440d0b9acc42f1", // payment_id_hex 0 // flags }, - { - // new format normal address with custom flags - "ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3APrDvRoL5C", // address - "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key - "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key - "", // payment_id_hex - 0xfe // flags - }, - { - // new format integrated address with custom flags - "iZ4mBxubNfqcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTrG7nU5rRCWmcozLaMoY95sAbo6", // address - "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key - "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key - "3ba0527bcfb1fa93630d28eed6", // payment_id - 0xfe // flags - }, { // normal auditable address - "aZxb9Et6FhP9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJMEmqQFn", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "", // payment_id @@ -604,7 +599,7 @@ addr_entry_t addr_entries[] = }, { // auditable integrated address - "aiZXDondHWu9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJM9xJH8EbjuRiMJgFmPRATsEV9", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "3ba0527bcfb1fa93630d28eed6", // payment_id @@ -614,16 +609,34 @@ addr_entry_t addr_entries[] = void check_add_entry(const addr_entry_t& ae) { - std::string payment_id, payment_id_hex; - currency::account_public_address addr = AUTO_VAL_INIT(addr); + // Build address struct from hex keys + currency::account_public_address addr_built = AUTO_VAL_INIT(addr_built); + ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(ae.view_pub_key, addr_built.view_public_key)); + ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(ae.spend_pub_key, addr_built.spend_public_key)); + addr_built.flags = ae.flags; - ASSERT_TRUE(currency::get_account_address_and_payment_id_from_str(addr, payment_id, ae.address)); - payment_id_hex = epee::string_tools::buff_to_hex_nodelimer(payment_id); + // Encode to string (with or without payment id) + std::string payment_id_bin; + if (!ae.payment_id_hex.empty()) + epee::string_tools::parse_hexstr_to_binbuff(ae.payment_id_hex, payment_id_bin); - ASSERT_EQ(ae.flags, addr.flags); - ASSERT_EQ(ae.payment_id_hex, payment_id_hex); - ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr.view_public_key)); - ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr.spend_public_key)); + std::string addr_str = payment_id_bin.empty() + ? currency::get_account_address_as_str(addr_built) + : currency::get_account_address_and_payment_id_as_str(addr_built, payment_id_bin); + + ASSERT_FALSE(addr_str.empty()); + + // Round-trip: parse the generated address back + currency::account_public_address addr_parsed = AUTO_VAL_INIT(addr_parsed); + std::string payment_id_parsed; + ASSERT_TRUE(currency::get_account_address_and_payment_id_from_str(addr_parsed, payment_id_parsed, addr_str)); + + std::string payment_id_parsed_hex = epee::string_tools::buff_to_hex_nodelimer(payment_id_parsed); + + ASSERT_EQ(ae.flags, addr_parsed.flags); + ASSERT_EQ(ae.payment_id_hex, payment_id_parsed_hex); + ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr_parsed.view_public_key)); + ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr_parsed.spend_public_key)); } TEST(auditable_addresses, basic) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index fd0807b5..59c36829 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -10,6 +10,7 @@ #include #include "currency_core/currency_basic.h" #include "currency_core/currency_format_utils.h" +#include "currency_core/account.h" #include "serialization/serialization.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" @@ -727,7 +728,7 @@ TEST(Serialization, serializes_transacion_versions) c.comment = "sdcwdcwcewdcecevthbtg"; tx.extra.push_back(c); extra_alias_entry eae = AUTO_VAL_INIT(eae); - currency::get_account_address_from_str(eae.m_address, "ZxDcDWmA7Yj32srfjMHAY6WPzBB8uqpvzKxEsAjDZU6NRg1yZsRfmr87mLXCvMRHXd5n2kdRWhbqA3WWTbEW4jLd1XxL46tnv"); + { currency::account_base tmp_acc; tmp_acc.generate(); eae.m_address = tmp_acc.get_keys().account_address; } eae.m_alias = "eokcmeockme"; eae.m_text_comment = "sdssccsc"; tx.extra.push_back(eae); From 1545a803352fe59d01aa71d0cbc1910bd5c41fd1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 15:02:28 +0000 Subject: [PATCH 07/10] ci: add Forgejo Actions build & release workflow Add .forgejo/workflows/build.yml that builds the Lethean blockchain on push to dev/main with workflow_dispatch support. Builds testnet on dev, mainnet on main, creates Forgejo releases with RPM/tar.xz/zip packages and SHA256 checksums. - CPU limited to 3 cores (~10% of 32-thread host) via Docker --cpus cap - Private submodule auth via git credential store with RELEASE_TOKEN - SDK caching with actions/cache for Conan build artifacts - CMake installed via pip for preset version 8 support - Tags fetched for libmdbx git describe version detection - Fix missing contrib/randomx entry in .gitmodules Co-Authored-By: Claude Opus 4.6 --- .forgejo/workflows/build.yml | 116 +++++++++++++++++++++++++++++++++++ .gitmodules | 3 + 2 files changed, 119 insertions(+) create mode 100644 .forgejo/workflows/build.yml diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml new file mode 100644 index 00000000..84f51ffd --- /dev/null +++ b/.forgejo/workflows/build.yml @@ -0,0 +1,116 @@ +name: Build & Release +on: + workflow_dispatch: + push: + branches: + - dev + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CPU_CORES: "3" + FORGEJO_URL: ${{ github.server_url }} + +jobs: + build: + name: Build ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + submodules: false + + - name: Init submodules + run: | + git config --global url."http://claude:${{ secrets.RELEASE_TOKEN }}@forgejo-mock:3000/".insteadOf "http://forge.snider.dev:4000/" + git submodule update --init --recursive + git submodule foreach --recursive 'git fetch --tags origin 2>/dev/null || true' + + - name: Install build dependencies + run: | + apt-get update + apt-get install -y build-essential git python3 python3-pip autotools-dev pkg-config rpm + pip install cmake --break-system-packages + + - name: Restore SDK cache + id: cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/build/sdk + ${{ github.workspace }}/build/bin + key: ${{ runner.os }}-${{ runner.arch }}-sdk + + - name: Build dependencies + run: make build-deps CPU_CORES=${{ env.CPU_CORES }} + + - name: Compile + run: make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} CPU_CORES=${{ env.CPU_CORES }} + + - name: Compute release tag + id: release + run: | + VERSION=$(grep '^BUILD_VERSION:=' Makefile | cut -d'=' -f2) + if [ "${{ github.ref_name }}" = "main" ]; then + TAG="v${VERSION}+${{ github.run_number }}" + PRERELEASE=false + TITLE="v${VERSION} (build ${{ github.run_number }})" + else + TAG="v${VERSION}-beta+${{ github.run_number }}" + PRERELEASE=true + TITLE="v${VERSION}-beta (build ${{ github.run_number }})" + fi + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT" + echo "title=${TITLE}" >> "$GITHUB_OUTPUT" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + + - name: Create release + id: create_release + run: | + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"${{ steps.release.outputs.tag }}\", + \"target_commitish\": \"${{ github.sha }}\", + \"name\": \"${{ steps.release.outputs.title }}\", + \"body\": \"Automated build from \`${{ github.ref_name }}\` branch (run #${{ github.run_number }})\", + \"draft\": false, + \"prerelease\": ${{ steps.release.outputs.prerelease }} + }") + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + if [ "$HTTP_CODE" -ge 400 ]; then + echo "Failed to create release (HTTP ${HTTP_CODE}):" + echo "$BODY" + exit 1 + fi + RELEASE_ID=$(echo "$BODY" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") + echo "id=${RELEASE_ID}" >> "$GITHUB_OUTPUT" + echo "Created release ID: ${RELEASE_ID}" + + - name: Upload release assets + run: | + for file in build/packages/lethean-*; do + [ -f "$file" ] || continue + FILENAME=$(basename "$file") + echo "Uploading: ${FILENAME}" + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ + "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.id }}/assets?name=${FILENAME}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary "@${file}") + if [ "$HTTP_CODE" -ge 400 ]; then + echo "Failed to upload ${FILENAME} (HTTP ${HTTP_CODE})" + exit 1 + fi + echo "Uploaded ${FILENAME} (HTTP ${HTTP_CODE})" + done diff --git a/.gitmodules b/.gitmodules index 29514a7f..5c7489d2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,9 @@ [submodule "docs"] path = docs url = https://github.com/letheanVPN/documentation.git +[submodule "contrib/randomx"] + path = contrib/randomx + url = https://github.com/tevador/RandomX.git [submodule ".core/build"] path = .core/build url = http://forge.snider.dev:4000/host-uk/build.git From f3df50cba5776b96634ed837233741e51abe2d81 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 15:03:13 +0000 Subject: [PATCH 08/10] ci: prune old releases to keep only latest per branch After uploading new release assets, delete older beta releases (dev) or older stable releases (main) to conserve disk. Only the latest build per branch is kept on the Forgejo instance; older builds are available on mirrored free hosting. Co-Authored-By: Claude Opus 4.6 --- .forgejo/workflows/build.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 84f51ffd..1d60106e 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -114,3 +114,36 @@ jobs: fi echo "Uploaded ${FILENAME} (HTTP ${HTTP_CODE})" done + + - name: Prune old releases + run: | + TAG_PREFIX="${{ github.ref_name == 'main' && 'v6' || 'v6.*-beta' }}" + CURRENT_ID="${{ steps.create_release.outputs.id }}" + echo "Keeping release ${CURRENT_ID}, pruning older ${TAG_PREFIX} releases..." + PAGE=1 + while true; do + RELEASES=$(curl -s "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases?limit=50&page=${PAGE}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}") + COUNT=$(echo "$RELEASES" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo 0) + [ "$COUNT" = "0" ] && break + echo "$RELEASES" | python3 -c " + import sys, json, fnmatch + for r in json.load(sys.stdin): + rid = r['id'] + tag = r['tag_name'] + if rid == ${CURRENT_ID}: + continue + if fnmatch.fnmatch(tag, '${TAG_PREFIX}*'): + print(f'{rid} {tag}') + " | while read -r RID RTAG; do + echo "Deleting release ${RTAG} (id=${RID})" + curl -s -o /dev/null -X DELETE \ + "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases/${RID}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" + curl -s -o /dev/null -X DELETE \ + "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/tags/${RTAG}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" + done + PAGE=$((PAGE + 1)) + done + echo "Prune complete" From 22c0c5a251ffd83245343573e47773c1c69fe3cc Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 15:06:47 +0000 Subject: [PATCH 09/10] ci: use Forgejo Conan registry instead of actions/cache Pull pre-built dependencies from the Forgejo package registry (conan_build remote) instead of caching the SDK locally. Removes the 570MB actions/cache overhead since all packages are already available as pre-compiled binaries on the registry. Co-Authored-By: Claude Opus 4.6 --- .forgejo/workflows/build.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 1d60106e..fe737c29 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -38,20 +38,11 @@ jobs: apt-get install -y build-essential git python3 python3-pip autotools-dev pkg-config rpm pip install cmake --break-system-packages - - name: Restore SDK cache - id: cache - uses: actions/cache@v4 - with: - path: | - ${{ github.workspace }}/build/sdk - ${{ github.workspace }}/build/bin - key: ${{ runner.os }}-${{ runner.arch }}-sdk - - name: Build dependencies - run: make build-deps CPU_CORES=${{ env.CPU_CORES }} + run: make build-deps CPU_CORES=${{ env.CPU_CORES }} CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan CONAN_USER=claude CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} - name: Compile - run: make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} CPU_CORES=${{ env.CPU_CORES }} + run: make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} CPU_CORES=${{ env.CPU_CORES }} CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan CONAN_USER=claude CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} - name: Compute release tag id: release From c9ad4de9bb42393b4a92bb07c2481f460339e7b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Feb 2026 19:37:35 +0000 Subject: [PATCH 10/10] ci: multi-platform Forgejo Actions pipeline Build Linux x86_64 (snider-linux) and macOS ARM64 (M3 Ultra) natively via self-hosted forgejo-runner, aggregate into single release. Co-Authored-By: Claude Opus 4.6 --- .forgejo/workflows/build.yml | 174 +++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 40 deletions(-) mode change 100644 => 100755 .forgejo/workflows/build.yml diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml old mode 100644 new mode 100755 index fe737c29..778cd7bd --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -1,4 +1,5 @@ name: Build & Release + on: workflow_dispatch: push: @@ -11,16 +12,20 @@ concurrency: cancel-in-progress: true env: - CPU_CORES: "3" FORGEJO_URL: ${{ github.server_url }} jobs: - build: - name: Build ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} - runs-on: ubuntu-latest + # ────────────────────────────────────────────── + # Linux x86_64 — native build on snider-linux + # ────────────────────────────────────────────── + build-linux: + name: Linux x86_64 + runs-on: linux-native + env: + CPU_CORES: "24" steps: - name: Checkout - uses: actions/checkout@v4 + uses: https://code.forgejo.org/actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true @@ -28,21 +33,108 @@ jobs: - name: Init submodules run: | - git config --global url."http://claude:${{ secrets.RELEASE_TOKEN }}@forgejo-mock:3000/".insteadOf "http://forge.snider.dev:4000/" - git submodule update --init --recursive + git config --global url."${{ env.FORGEJO_URL }}/".insteadOf "http://forge.snider.dev:4000/" + git -c http.extraHeader="Authorization: token ${{ secrets.RELEASE_TOKEN }}" submodule update --init --recursive git submodule foreach --recursive 'git fetch --tags origin 2>/dev/null || true' - - name: Install build dependencies - run: | - apt-get update - apt-get install -y build-essential git python3 python3-pip autotools-dev pkg-config rpm - pip install cmake --break-system-packages - - name: Build dependencies - run: make build-deps CPU_CORES=${{ env.CPU_CORES }} CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan CONAN_USER=claude CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} + run: | + make build-deps \ + CPU_CORES=${{ env.CPU_CORES }} \ + CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan \ + CONAN_USER=claude \ + CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} - name: Compile - run: make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} CPU_CORES=${{ env.CPU_CORES }} CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan CONAN_USER=claude CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} + run: | + make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} \ + CPU_CORES=${{ env.CPU_CORES }} \ + CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan \ + CONAN_USER=claude \ + CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} + + - name: Upload artifacts + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: linux-x86_64 + path: build/packages/lethean-* + retention-days: 7 + + # ────────────────────────────────────────────── + # macOS ARM64 — native build on M3 Ultra + # ────────────────────────────────────────────── + build-macos: + name: macOS ARM64 + runs-on: macos-native + env: + CPU_CORES: "24" + steps: + - name: Checkout + uses: https://code.forgejo.org/actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + submodules: false + + - name: Init submodules + run: | + git config --global url."${{ env.FORGEJO_URL }}/".insteadOf "http://forge.snider.dev:4000/" + git -c http.extraHeader="Authorization: token ${{ secrets.RELEASE_TOKEN }}" submodule update --init --recursive + git submodule foreach --recursive 'git fetch --tags origin 2>/dev/null || true' + + - name: Build dependencies + run: | + export PATH="/opt/homebrew/bin:$PATH" + make build-deps \ + CPU_CORES=${{ env.CPU_CORES }} \ + CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan \ + CONAN_USER=claude \ + CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} + + - name: Compile + run: | + export PATH="/opt/homebrew/bin:$PATH" + make ${{ github.ref_name == 'main' && 'mainnet' || 'testnet' }} \ + CPU_CORES=${{ env.CPU_CORES }} \ + CONAN_URL=${{ env.FORGEJO_URL }}/api/packages/host-uk/conan \ + CONAN_USER=claude \ + CONAN_PASSWORD=${{ secrets.RELEASE_TOKEN }} + + - name: Upload artifacts + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: macos-arm64 + path: build/packages/lethean-* + retention-days: 7 + + # ────────────────────────────────────────────── + # Aggregate artifacts and create release + # ────────────────────────────────────────────── + release: + name: Create Release + needs: [build-linux, build-macos] + runs-on: linux-native + steps: + - name: Checkout + uses: https://code.forgejo.org/actions/checkout@v4 + with: + fetch-depth: 1 + submodules: false + + - name: Download Linux artifacts + uses: https://code.forgejo.org/forgejo/download-artifact@v4 + with: + name: linux-x86_64 + path: artifacts/linux + + - name: Download macOS artifacts + uses: https://code.forgejo.org/forgejo/download-artifact@v4 + with: + name: macos-arm64 + path: artifacts/macos + + - name: List artifacts + run: find artifacts/ -type f | sort - name: Compute release tag id: release @@ -66,14 +158,14 @@ jobs: id: create_release run: | RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ - "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases" \ + "${{ env.FORGEJO_URL }}/api/v1/repos/${{ github.repository }}/releases" \ -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{ \"tag_name\": \"${{ steps.release.outputs.tag }}\", \"target_commitish\": \"${{ github.sha }}\", \"name\": \"${{ steps.release.outputs.title }}\", - \"body\": \"Automated build from \`${{ github.ref_name }}\` branch (run #${{ github.run_number }})\", + \"body\": \"Automated build from \`${{ github.ref_name }}\` branch (run #${{ github.run_number }})\n\n## Platforms\n- Linux x86_64\n- macOS ARM64 (Apple Silicon)\", \"draft\": false, \"prerelease\": ${{ steps.release.outputs.prerelease }} }") @@ -90,20 +182,22 @@ jobs: - name: Upload release assets run: | - for file in build/packages/lethean-*; do - [ -f "$file" ] || continue - FILENAME=$(basename "$file") - echo "Uploading: ${FILENAME}" - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ - "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.id }}/assets?name=${FILENAME}" \ - -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ - -H "Content-Type: application/octet-stream" \ - --data-binary "@${file}") - if [ "$HTTP_CODE" -ge 400 ]; then - echo "Failed to upload ${FILENAME} (HTTP ${HTTP_CODE})" - exit 1 - fi - echo "Uploaded ${FILENAME} (HTTP ${HTTP_CODE})" + for dir in artifacts/linux artifacts/macos; do + for file in "$dir"/lethean-*; do + [ -f "$file" ] || continue + FILENAME=$(basename "$file") + echo "Uploading: ${FILENAME}" + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ + "${{ env.FORGEJO_URL }}/api/v1/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.id }}/assets?name=${FILENAME}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary "@${file}") + if [ "$HTTP_CODE" -ge 400 ]; then + echo "Failed to upload ${FILENAME} (HTTP ${HTTP_CODE})" + exit 1 + fi + echo "Uploaded ${FILENAME} (HTTP ${HTTP_CODE})" + done done - name: Prune old releases @@ -113,7 +207,7 @@ jobs: echo "Keeping release ${CURRENT_ID}, pruning older ${TAG_PREFIX} releases..." PAGE=1 while true; do - RELEASES=$(curl -s "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases?limit=50&page=${PAGE}" \ + RELEASES=$(curl -s "${{ env.FORGEJO_URL }}/api/v1/repos/${{ github.repository }}/releases?limit=50&page=${PAGE}" \ -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}") COUNT=$(echo "$RELEASES" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo 0) [ "$COUNT" = "0" ] && break @@ -127,14 +221,14 @@ jobs: if fnmatch.fnmatch(tag, '${TAG_PREFIX}*'): print(f'{rid} {tag}') " | while read -r RID RTAG; do - echo "Deleting release ${RTAG} (id=${RID})" - curl -s -o /dev/null -X DELETE \ - "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/releases/${RID}" \ - -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" - curl -s -o /dev/null -X DELETE \ - "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}/tags/${RTAG}" \ - -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" - done + echo "Deleting release ${RTAG} (id=${RID})" + curl -s -o /dev/null -X DELETE \ + "${{ env.FORGEJO_URL }}/api/v1/repos/${{ github.repository }}/releases/${RID}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" + curl -s -o /dev/null -X DELETE \ + "${{ env.FORGEJO_URL }}/api/v1/repos/${{ github.repository }}/tags/${RTAG}" \ + -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" + done PAGE=$((PAGE + 1)) done echo "Prune complete"