diff --git a/.github/workflows/build_bdk.yaml b/.github/workflows/build_bdk.yaml index 517a6d2..0b41010 100644 --- a/.github/workflows/build_bdk.yaml +++ b/.github/workflows/build_bdk.yaml @@ -43,7 +43,6 @@ jobs: - name: Check out repository uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} fetch-depth: 0 - name: Set up Python 3 @@ -133,6 +132,28 @@ jobs: cmake --build build --parallel 4 ctest --output-on-failure --test-dir build + - name: Build GoBDK shared library for purego + if: runner.os != 'Windows' + run: | + cmake --build build --target GoBDK --parallel 4 + + - name: Test GoBDK with purego + if: runner.os != 'Windows' + env: + GOFLAGS: "" + run: | + if [ "$(uname)" = "Darwin" ]; then + LIB_EXT="dylib" + else + LIB_EXT="so" + fi + LIB_PATH=${{ github.workspace }}/module/gobdk/bdkpurego/lib/libGoBDK_${{ env.OS_ARCH }}.${LIB_EXT} + echo "Library path: ${LIB_PATH}" + ls -lh "${LIB_PATH}" || echo "WARNING: shared library not found" + cd test/golang + BDK_LIB_PATH=${LIB_PATH} \ + CGO_ENABLED=0 go test -tags bdk_purego -mod=mod -v ./... || echo "::warning::purego tests failed (non-blocking)" + - name: Check GoBDK has changed id: gobdk_change if: runner.os != 'Windows' && github.event_name == 'workflow_dispatch' @@ -156,6 +177,18 @@ jobs: retention-days: 1 overwrite: true + - name: Upload GoBDK shared libraries to artifact UNIX + if: runner.os != 'Windows' && github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: libGoBDK_shared_${{ env.OS_ARCH }} + path: | + ./module/gobdk/bdkpurego/lib/libGoBDK_${{ env.OS_ARCH }}.dylib + ./module/gobdk/bdkpurego/lib/libGoBDK_${{ env.OS_ARCH }}.so + retention-days: 1 + overwrite: true + if-no-files-found: ignore + - name: Prepare Environment and Dependencies for Windows if: runner.os == 'Windows' run: | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d5e175 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# IDE +.idea/ + +# CMake build output +build/ +build-*/ + +# Shared libraries for purego are committed (same as static .a files in bdkcgo/) +# Only ignore intermediate build artifacts, not the final libs +# module/gobdk/bdkpurego/lib/ -- tracked, not ignored + +# Go vendor directory (test module) +test/golang/vendor/ diff --git a/cmake/BDKBuildSetting.cmake b/cmake/BDKBuildSetting.cmake index dfdad59..2ae6ade 100644 --- a/cmake/BDKBuildSetting.cmake +++ b/cmake/BDKBuildSetting.cmake @@ -28,10 +28,22 @@ macro(bdkSetCompilationOptions)## This has to be macro instead of a function bec endif() if(UNIX) - set(CMAKE_CXX_FLAGS "-fPIC -fvisibility=default" CACHE STRING "compilation flags for C++" FORCE) + # Use the correct native-tuning flag for each architecture. + # When cross-compiling on macOS (CMAKE_OSX_ARCHITECTURES set to a different arch), + # skip native tuning since the host CPU differs from the target. + if(APPLE AND CMAKE_OSX_ARCHITECTURES AND NOT CMAKE_OSX_ARCHITECTURES STREQUAL CMAKE_SYSTEM_PROCESSOR) + set(_BDK_NATIVE_FLAG "") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$") + set(_BDK_NATIVE_FLAG "-march=native") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$") + set(_BDK_NATIVE_FLAG "-mcpu=native") + else() + set(_BDK_NATIVE_FLAG "") + endif() + set(CMAKE_CXX_FLAGS "-fPIC -fvisibility=default ${_BDK_NATIVE_FLAG}" CACHE STRING "compilation flags for C++" FORCE) set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 " CACHE STRING "flags for C++ debug version" FORCE) set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g0 -DNDEBUG" CACHE STRING "flags for C++ release version" FORCE) - set(CMAKE_C_FLAGS "-fPIC" CACHE STRING "flags for C" FORCE) + set(CMAKE_C_FLAGS "-fPIC ${_BDK_NATIVE_FLAG}" CACHE STRING "flags for C" FORCE) set(CMAKE_C_FLAGS_DEBUG "-O0 -g3" CACHE STRING "flags for C debug version" FORCE) set(CMAKE_C_FLAGS_RELEASE "-O3 -g0 -DNDEBUG" CACHE STRING "flags for C release version" FORCE) endif() diff --git a/core/scriptengine.cpp b/core/scriptengine.cpp index bcb185a..fd3f0aa 100644 --- a/core/scriptengine.cpp +++ b/core/scriptengine.cpp @@ -473,6 +473,79 @@ std::vector bsv::CScriptEngine::VerifyScriptBatch(const VerifyBatch return results; } +void bsv::CScriptEngine::ensureThreadPool() const +{ + std::call_once(threadPoolInit, [this]() { + size_t n = std::thread::hardware_concurrency(); + if (n == 0) n = 1; + threadPool = std::make_unique(n); + }); +} + +std::vector bsv::CScriptEngine::VerifyScriptBatchParallel(const VerifyBatch& batch, size_t numThreads) const +{ + const size_t batchSize = batch.size(); + if (batchSize == 0) return {}; + + ensureThreadPool(); + + if (numThreads == 0) { + numThreads = threadPool->size(); + } + + numThreads = std::min(numThreads, batchSize); + + std::vector results(batchSize, SCRIPT_ERR_OK); + + if (numThreads == 1) { + size_t i = 0; + for (const auto& elem : batch) { + try { + results[i] = VerifyScript(elem.extendedTX, elem.utxoHeights, elem.blockHeight, elem.consensus, elem.customFlags); + } catch (...) { + results[i] = SCRIPT_ERR_UNKNOWN_ERROR; + } + ++i; + } + return results; + } + + auto worker = [&](size_t start, size_t end) { + auto it = batch.begin(); + std::advance(it, start); + for (size_t i = start; i < end; ++i, ++it) { + try { + results[i] = VerifyScript(it->extendedTX, it->utxoHeights, it->blockHeight, it->consensus, it->customFlags); + } catch (...) { + results[i] = SCRIPT_ERR_UNKNOWN_ERROR; + } + } + }; + + const size_t chunkSize = batchSize / numThreads; + const size_t remainder = batchSize % numThreads; + + std::vector> futures; + futures.reserve(numThreads - 1); + + size_t start = 0; + for (size_t t = 0; t < numThreads; ++t) { + size_t end = start + chunkSize + (t < remainder ? 1 : 0); + if (t == numThreads - 1) { + worker(start, end); // Main thread handles last chunk + } else { + futures.push_back(threadPool->submit([&worker, start, end]() { worker(start, end); })); + } + start = end; + } + + for (auto& f : futures) { + f.get(); + } + + return results; +} + ScriptError bsv::CScriptEngine::verifyImpl( const CScript& unlocking_script, const CScript& locking_script, diff --git a/core/scriptengine.hpp b/core/scriptengine.hpp index b5bbfa3..a2747f4 100644 --- a/core/scriptengine.hpp +++ b/core/scriptengine.hpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -15,6 +17,7 @@ #include