Add reproducible-build feature; fix DARCH→ARCH; std::fesetround compat#1
Add reproducible-build feature; fix DARCH→ARCH; std::fesetround compat#1kim0 wants to merge 1 commit into
Conversation
Two related improvements that let downstreams ship deterministic, byte-
reproducible binaries linked against randomx-rs without forcing every
miner to give up native CPU codegen:
1. New `reproducible-build` Cargo feature (off by default). When on,
the bundled RandomX is built with `-DARCH=default`, omitting the
`-march=native` / CPU-specific instructions and producing the same
object code on every host. Hash-rate-sensitive miners simply leave
the feature off and get current upstream behaviour.
2. The cmake invocation previously passed `.define("DARCH", "native")`,
which sets a CMake variable literally named `DARCH`. RandomX's own
CMakeLists.txt only consults `ARCH`. Result: every existing build of
randomx-rs has been shipping whatever RandomX's CMake default
happens to be, not the `native` mode that build.rs intends. Fixed
to pass `.define("ARCH", ...)`.
3. Apply a build-time patch to RandomX/src/instructions_portable.cpp
qualifying the single `fesetround(mode)` call as `std::fesetround`.
Under libstdc++ configurations where the global namespace symbol
isn't reliably exposed (e.g. Guix's gcc-15.2 with _GLIBCXX_HAVE_FENV_H
undef'd in c++config.h), the unqualified call fails to compile;
qualifying it is a no-op everywhere it already worked.
cuprated's reproducible-build pipeline (contrib/guix) needs RandomX
built without -march=native so the produced binary is identical
across CPUs. randomx-rs upstream defaults to native (correct for
miners, hostile to reproducible release artifacts).
Point at kim0/randomx-rs branch feat/reproducible-build-flag, which
adds an opt-in \`reproducible-build\` Cargo feature that flips
RandomX's CMake ARCH to "default", plus two bug fixes that travel
with it:
- DARCH -> ARCH: the previous .define(\"DARCH\", \"native\") in
randomx-rs build.rs set a CMake variable RandomX never reads.
Every existing build of the crate has been shipping whatever
RandomX's CMake default happened to be. Strict bug fix.
- std::fesetround compat patch applied at build time to
RandomX/src/instructions_portable.cpp. Lets the crate compile
under libstdc++ configurations where <cfenv> doesn't expose
fesetround in the global namespace.
Both PRs (kim0/randomx-rs#1 here, and the
cuprate PR using it) are intended for eventual upstream into
Cuprate/randomx-rs and cuprate-crypto/cuprate respectively.
|
Closing this PR — after digging deeper into RandomX's CMake, the changes here turn out to be unnecessary. The root finding: The if(NOT ARCH)
set(ARCH "default")
endif()
...
if(ARCH STREQUAL "native")
add_flag("-march=native")So Implications for the consumer side (cuprate): the build is already reproducible across CPUs given a pinned toolchain. The Guix pipeline I'm proposing on the cuprate side handles the only real issue (libstdc++'s What I'm doing instead: the cuprate PR (kim0/cuprate#2) is being restructured to depend on upstream The branch Thanks to two GPT-pro reviews for catching this — both flagged the inconsistency that the comment in the new build.rs claimed RandomX reads |
Motivation
Let downstreams that need reproducible, byte-identical release binaries
(notably the cuprate Monero node) opt out of the
-march=nativeCPU-specific codegen that miners want, without forking the crate.
Changes
New
reproducible-buildCargo feature (off by default). When on,the bundled RandomX is built with
-DARCH=default, producing thesame object code on every host. Miners leave it off and keep current
behaviour.
Fix
DARCH→ARCHin.define(...). The previous call set aCMake variable literally named
DARCH, which RandomX's CMakeListsdoesn't read; only
ARCHis consulted. Every existing build of thiscrate has been getting whatever RandomX's CMake
ARCHdefault is,not the
nativemode the build script thinks it's selecting. Thisis a strict bug fix.
std::fesetroundcompat patch applied at build time toRandomX/src/instructions_portable.cpp. Under libstdc++configurations where the global-namespace
fesetroundisn't reliablyexposed via
<cfenv>(e.g. Guix's gcc-15.2 with_GLIBCXX_HAVE_FENV_Hundefined inc++config.h), the unqualifiedcall fails to compile. Qualifying it as
std::fesetroundworkseverywhere
<cfenv>works and is a no-op on toolchains that alreadyexposed the symbol globally. Applied as a build-script patch so the
RandomX submodule pointer stays at the tevador upstream commit;
becomes a no-op once tevador/RandomX picks up the same fix.
Verification
Built with the
reproducible-buildfeature in the cuprate Guixreproducible-build pipeline; 5 independent builds across separate pods
produced byte-identical
cupratedartifacts (SHA256cc1f523a68bf61304c0c4b8fba4352aaea4626491460e95795b3e5b0f35c15d0).Upstreaming
This branch lives in
kim0/randomx-rs. Intent is to keep it open herewhile the matching cuprate PR is reviewed, then upstream to
Cuprate/randomx-rs:developmentonce both are agreed upon.