Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ jobs:
if: |
vars.SKIP_LINUX64 == '' ||
vars.SKIP_LINUX64_ASAN == '' ||
vars.SKIP_LINUX64_FUZZ == '' ||
vars.SKIP_LINUX64_SQLITE == ''
vars.SKIP_LINUX64_FUZZ == ''
with:
build-target: linux64
container-path: ${{ needs.container.outputs.path }}
Expand Down Expand Up @@ -170,6 +169,17 @@ jobs:
base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

depends-linux64_platform_gui:
name: x86_64-pc-linux-gnu_platform_gui
uses: ./.github/workflows/build-depends.yml
needs: [check-skip, container, cache-sources]
if: ${{ vars.SKIP_LINUX64_SQLITE == '' }}
with:
build-target: linux64_platform_gui
container-path: ${{ needs.container.outputs.path }}
base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

depends-mac:
name: x86_64-apple-darwin
uses: ./.github/workflows/build-depends.yml
Expand Down Expand Up @@ -285,15 +295,15 @@ jobs:
src-linux64_sqlite:
name: linux64_sqlite-build
uses: ./.github/workflows/build-src.yml
needs: [check-skip, container, depends-linux64]
needs: [check-skip, container, depends-linux64_platform_gui]
if: ${{ vars.SKIP_LINUX64_SQLITE == '' }}
with:
build-target: linux64_sqlite
container-path: ${{ needs.container.outputs.path }}
depends-key: ${{ needs.depends-linux64.outputs.key }}
depends-host: ${{ needs.depends-linux64.outputs.host }}
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }}
depends-key: ${{ needs.depends-linux64_platform_gui.outputs.key }}
depends-host: ${{ needs.depends-linux64_platform_gui.outputs.host }}
depends-dep-opts: ${{ needs.depends-linux64_platform_gui.outputs.dep-opts }}
depends-artifact: ${{ needs.depends-linux64_platform_gui.outputs.built-artifact }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

src-linux64_tsan:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ share/qt/Info.plist

src/qt/*.moc
src/qt/moc_*.cpp
src/qt/platform/moc_*.cpp
src/qt/forms/ui_*.h

src/qt/test/moc*.cpp
Expand Down
4 changes: 4 additions & 0 deletions ci/dash/build_src.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ if [ "${RUN_STDLIB_PATH_CHECK}" = "true" ]; then
make -C src --jobs=1 check-stdlib-paths
fi

if [ "${RUN_CHECK_NO_RUST}" = "true" ]; then
"${BASE_ROOT_DIR}/contrib/devtools/check-no-rust.py" src/dashd src/dash-cli src/dash-tx src/dash-wallet src/test/fuzz/fuzz
fi

if [ -n "$USE_VALGRIND" ]; then
echo "valgrind in USE!"
"${BASE_ROOT_DIR}/ci/test/wrap-valgrind.sh"
Expand Down
2 changes: 2 additions & 0 deletions ci/dash/matrix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ elif [ "$BUILD_TARGET" = "linux64_multiprocess" ]; then
source ./ci/test/00_setup_env_native_multiprocess.sh
elif [ "$BUILD_TARGET" = "linux64_nowallet" ]; then
source ./ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh
elif [ "$BUILD_TARGET" = "linux64_platform_gui" ]; then
source ./ci/test/00_setup_env_native_platform_gui.sh
elif [ "$BUILD_TARGET" = "linux64_sqlite" ]; then
source ./ci/test/00_setup_env_native_sqlite.sh
elif [ "$BUILD_TARGET" = "linux64_tsan" ]; then
Expand Down
12 changes: 12 additions & 0 deletions ci/test/00_setup_env_native_platform_gui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
#
# Copyright (c) 2026 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

export LC_ALL=C.UTF-8

# Depends with the Dash Platform CXX bindings; linux64_sqlite builds against it.
export CONTAINER_NAME=ci_native_platform_gui
export HOST=x86_64-pc-linux-gnu
export DEP_OPTS="PLATFORM_GUI=1"
1 change: 1 addition & 0 deletions ci/test/00_setup_env_native_sqlite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libhar
export DEP_OPTS="NO_BDB=1 NO_UPNP=1 DEBUG=1"
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --with-sqlite --without-bdb CC=gcc-11 CXX=g++-11"
export RUN_CHECK_NO_RUST="true"
57 changes: 57 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ if test "$enable_miner" = "yes"; then
AC_DEFINE(ENABLE_MINER, 1, [Define this symbol if in-wallet miner should be enabled])
fi

AC_ARG_ENABLE([platform-gui],
[AS_HELP_STRING([--enable-platform-gui],
[enable Dash Platform (usernames, DashPay contacts) in the GUI; requires the GUI, the wallet and the Dash Platform CXX bindings built by depends with PLATFORM_GUI=1 (default is no)])],
[enable_platform_gui=$enableval],
[enable_platform_gui=no])
AC_ARG_VAR([PLATFORM_CXX_CFLAGS], [C++ compiler flags for the Dash Platform CXX bindings])
AC_ARG_VAR([PLATFORM_CXX_LIBS], [Linker flags for the Dash Platform CXX bindings library (default: -ldash_platform_cxx); the system libraries it needs are always appended])

dnl Enable different -fsanitize options
AC_ARG_WITH([sanitizers],
[AS_HELP_STRING([--with-sanitizers],
Expand Down Expand Up @@ -1936,6 +1944,54 @@ if test "$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_
AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-fuzz(-binary) --enable-bench or --enable-tests])
fi

dnl The Dash Platform CXX bindings are a Rust static library linked into dash-qt
dnl (and its tests) only. PLATFORM_CXX_LIBS names the library (default
dnl -ldash_platform_cxx, from the depends prefix); the system libraries rustc
dnl reports for the archive (--print native-static-libs), less the C++ runtime,
dnl are appended to it.
if test "$enable_platform_gui" = "yes"; then
if test "$bitcoin_enable_qt" != "yes"; then
AC_MSG_ERROR([--enable-platform-gui requires the GUI])
fi
if test "$enable_wallet" != "yes"; then
AC_MSG_ERROR([--enable-platform-gui requires the wallet])
fi
if test -z "$PLATFORM_CXX_LIBS"; then
PLATFORM_CXX_LIBS="-ldash_platform_cxx"
fi
case $host in
*darwin*)
PLATFORM_CXX_LIBS="$PLATFORM_CXX_LIBS -framework Security -framework CoreFoundation"
;;
*mingw*)
PLATFORM_CXX_LIBS="$PLATFORM_CXX_LIBS -lbcrypt -ladvapi32 -lkernel32 -lntdll -luserenv -lws2_32 -ldbghelp"
;;
*)
PLATFORM_CXX_LIBS="$PLATFORM_CXX_LIBS -lpthread -ldl -lm"
;;
esac
TEMP_CPPFLAGS="$CPPFLAGS"
TEMP_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $PLATFORM_CXX_CFLAGS"
LIBS="$PLATFORM_CXX_LIBS $LIBS"
AC_LANG_PUSH([C++])
AC_MSG_CHECKING([for the Dash Platform CXX bindings])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <dash/platform/ffi.h>
]], [[
rust::Box<platform_ffi::PlatformClient> client{platform_ffi::new_platform_client(platform_ffi::Config{})};
client->shutdown();
]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
AC_MSG_ERROR([--enable-platform-gui requires the Dash Platform CXX bindings (build depends with PLATFORM_GUI=1)])])
AC_LANG_POP([C++])
CPPFLAGS="$TEMP_CPPFLAGS"
LIBS="$TEMP_LIBS"
AC_DEFINE([ENABLE_PLATFORM_GUI], [1], [Define this symbol to enable Dash Platform support in the GUI])
fi
AM_CONDITIONAL([ENABLE_PLATFORM_GUI], [test "$enable_platform_gui" = "yes"])

AM_CONDITIONAL([TARGET_DARWIN], [test "$TARGET_OS" = "darwin"])
AM_CONDITIONAL([BUILD_DARWIN], [test "$BUILD_OS" = "darwin"])
AM_CONDITIONAL([TARGET_LINUX], [test "$TARGET_OS" = "linux"])
Expand Down Expand Up @@ -2132,6 +2188,7 @@ echo " debug enabled = $enable_debug"
echo " stacktraces = $enable_stacktraces"
echo " crash hooks = $enable_crashhooks"
echo " miner enabled = $enable_miner"
echo " platform gui = $enable_platform_gui"
echo " werror = $enable_werror"
echo
echo " target os = $host_os"
Expand Down
34 changes: 34 additions & 0 deletions contrib/devtools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,37 @@ Example usage:

cd .../src
../contrib/devtools/circular-dependencies.py {*,*/*,*/*/*}.{h,cpp}

update-rust-hashes.py
=====================

Refreshes the sha256 pins of the prebuilt Rust toolchain
(`depends/packages/native_rust.mk`) and of the per-host standard libraries
(`depends/packages/rust_stdlib.mk`) after the version in `native_rust.mk` was
changed; each downloaded archive must match the `.sha256` file published next
to it. `--check` compares the pins with the published `.sha256` files instead
of rewriting them.

platform-bundle.sh
==================

Produces the crate bundle that `depends/packages/platform_cxx.mk` builds the
Dash Platform CXX bindings from, for a given `dashpay/platform` commit, and
prints the hashes to pin in `platform_cxx.mk`. The bundle includes the
Tenderdash source archive for the tag the commit's `Cargo.lock` pins, so the
two cannot disagree. Requires the Cargo version pinned in `native_rust.mk`,
GNU tar, GNU gzip (set `TAR` and `GZIP_PROG` if they are installed under other
names) and a `TMPDIR` with no `.cargo/config.toml` in any parent directory.
The output is reproducible: rerunning it for the same commit yields the same
bundle hash on any machine. The Platform tarball and the bundle are then
uploaded to the depends sources mirror.

contrib/devtools/platform-bundle.sh <platform-commit>

check-no-rust.py
================

Fails if any of the given executables contain Rust code. Run by the
`linux64_sqlite` CI job, which builds against depends with `PLATFORM_GUI=1`,
on `dashd`, the command-line tools and the fuzz binary, which must never link
the Dash Platform CXX bindings.
63 changes: 63 additions & 0 deletions contrib/devtools/check-no-rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright (c) 2026 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Check that executables do not link Rust code.

The Dash Platform CXX bindings (--enable-platform-gui) are for dash-qt only;
dashd, the command-line tools and the fuzz binary must not contain any of it.
A binary fails if its symbol table has cxx bridge, Rust runtime or Rust
standard library symbols, or has no symbols at all (a stripped binary cannot
be checked).

Example usage:

contrib/devtools/check-no-rust.py src/dashd src/dash-cli src/dash-tx src/dash-wallet src/test/fuzz/fuzz
'''
import re
import subprocess
import sys

from utils import determine_wellknown_cmd

# cxx bridge symbols carry "cxxbridge1$". Rust code is either legacy-mangled
# under a crate namespace (rust, std, core, alloc) or v0-mangled ("_R"
# followed by a path tag and a crate root "Cs<disambiguator>_"), which C and
# C++ symbols never match. The allocator shim, the panic handler and the
# unwinding personality are plain C names every Rust program links.
RUST_SYMBOL = re.compile(
r'cxxbridge1\$'
r'|_ZN(4rust|3std|4core|5alloc)[0-9]'
r'|\b_?_R[A-Za-z0-9_]*?Cs[A-Za-z0-9]*_[0-9]'
r'|\b_?(__rust_alloc|rust_begin_unwind|rust_eh_personality)'
)


def rust_symbols(nm, path):
result = subprocess.run(nm + [path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
if result.returncode != 0:
sys.exit(f'{path}: {result.stderr.strip()}')
lines = result.stdout.splitlines()
if not lines:
sys.exit(f'{path}: no symbols, so it cannot be checked (stripped?)')
return [line for line in lines if RUST_SYMBOL.search(line)]


def main():
if len(sys.argv) < 2:
sys.exit(__doc__)
nm = determine_wellknown_cmd('NM', 'nm')
failed = False
for path in sys.argv[1:]:
found = rust_symbols(nm, path)
if found:
failed = True
print(f'{path}: links Rust code, for example:')
for line in found[:5]:
print(f' {line}')
sys.exit(1 if failed else 0)


if __name__ == '__main__':
main()
Loading
Loading