mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-22 16:14:50 +01:00
Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d7d5f6b79 | ||
|
|
04a996b1a7 | ||
|
|
ed355b8f57 | ||
|
|
6c98d68be1 | ||
|
|
6d86b32e78 | ||
|
|
1dae0027cd | ||
|
|
9e59047a7e | ||
|
|
abb6ae2ec5 | ||
|
|
483d158f53 | ||
|
|
747a863f5b | ||
|
|
cc3cdbe921 | ||
|
|
c4082a45e6 | ||
|
|
185ca0e391 | ||
|
|
bc71372c0e | ||
|
|
bef4b1fdee | ||
|
|
ac940ac2ca | ||
|
|
8e5c02a77f | ||
|
|
ac4d0956cc | ||
|
|
454ac8e7db | ||
|
|
e9f73b8149 | ||
|
|
f22122bc27 | ||
|
|
7568bc3ab0 | ||
|
|
c065bcd2d7 | ||
|
|
6983c7d769 | ||
|
|
8769c718f4 | ||
|
|
ed0774bd08 | ||
|
|
f620dde411 | ||
|
|
b734c4026b | ||
|
|
7ea855fd55 | ||
|
|
dd47caee82 | ||
|
|
2a21824b11 | ||
|
|
57264431ff | ||
|
|
26294d627e | ||
|
|
b1499ddf8b | ||
|
|
d59ebac718 | ||
|
|
b83d4f7c57 | ||
|
|
b26c93a9df | ||
|
|
18f3ada037 | ||
|
|
187e3b89b5 | ||
|
|
ab58b2c0f8 | ||
|
|
d872277db5 | ||
|
|
2256f8965e | ||
|
|
5408e85145 | ||
|
|
338570de5c | ||
|
|
72675b8f55 | ||
|
|
b2cb203af0 | ||
|
|
39d53dd8bf | ||
|
|
577ddf6f5d | ||
|
|
a0a2b07701 | ||
|
|
e2c71c4fca | ||
|
|
c0d851e6c6 | ||
|
|
34576c4574 | ||
|
|
8f8c7cf7fa | ||
|
|
049bf100f1 | ||
|
|
3afd5a9729 | ||
|
|
23ac752d09 | ||
|
|
cd7937ce2d | ||
|
|
42d4847fdf | ||
|
|
96110f8846 | ||
|
|
729e4c2abd | ||
|
|
f8db6f6ce7 | ||
|
|
5e389959b9 | ||
|
|
c48d3a6ad8 | ||
|
|
73ed57a35e | ||
|
|
72d1141bd8 | ||
|
|
c9fa661131 | ||
|
|
a14e7b9dee | ||
|
|
d0f6d9953a | ||
|
|
d615eb6998 | ||
|
|
ae63cc4bf2 | ||
|
|
abcd4c4ff9 | ||
|
|
f725754615 | ||
|
|
d7c89ba74e | ||
|
|
d5e0077bef | ||
|
|
2869dae5ec | ||
|
|
71ee0163de | ||
|
|
4e869a67aa | ||
|
|
a2ac6cce57 | ||
|
|
e4b568917c | ||
|
|
f957c2171d | ||
|
|
1eb578045d | ||
|
|
e4f9ec2f05 | ||
|
|
3485252584 | ||
|
|
a3a1dcb589 | ||
|
|
fce1c60770 | ||
|
|
b75afaccb8 | ||
|
|
45703931e5 | ||
|
|
1e348bc55a | ||
|
|
4ec30d53ec | ||
|
|
72c1f13c33 | ||
|
|
7ebdfa2173 | ||
|
|
ecbcef33d6 | ||
|
|
8c973d6614 | ||
|
|
2378bbf356 | ||
|
|
acf7d53ace | ||
|
|
ce56548c63 | ||
|
|
5226a92f28 | ||
|
|
49d4ebcbfe | ||
|
|
0a80b1ae62 | ||
|
|
b8fb918969 | ||
|
|
792a75ac86 | ||
|
|
1bc3be1962 | ||
|
|
4b02bc1a72 | ||
|
|
52bcf62c0b | ||
|
|
33a0d4bb5b | ||
|
|
b7a722724d | ||
|
|
c9f751090c | ||
|
|
2327b2b0db | ||
|
|
26208b3a0c | ||
|
|
3ae592537d | ||
|
|
5dbb1bae38 | ||
|
|
c7faf72ac6 | ||
|
|
0a2afbeb77 | ||
|
|
75026cddea | ||
|
|
bbb4e118f3 | ||
|
|
b85dc7ed3a | ||
|
|
d2be9a22d8 | ||
|
|
ad6c13e041 | ||
|
|
35038b03c9 | ||
|
|
f7eded1dca | ||
|
|
6b19ede1a5 |
26
.github/actions/configure-docker/action.yml
vendored
26
.github/actions/configure-docker/action.yml
vendored
@@ -1,12 +1,24 @@
|
||||
name: 'Configure Docker'
|
||||
description: 'Set up Docker build driver and configure build cache args'
|
||||
inputs:
|
||||
use-cirrus:
|
||||
description: 'Use cirrus cache'
|
||||
cache-provider:
|
||||
description: 'gha or cirrus cache provider'
|
||||
required: true
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Check inputs
|
||||
shell: bash
|
||||
run: |
|
||||
# We expect only gha or cirrus as inputs to cache-provider
|
||||
case "${{ inputs.cache-provider }}" in
|
||||
gha|cirrus)
|
||||
;;
|
||||
*)
|
||||
echo "::warning title=Unknown input to configure docker action::Provided value was ${{ inputs.cache-provider }}"
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@@ -19,8 +31,12 @@ runs:
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
core.exportVariable('ACTIONS_CACHE_URL', process.env['ACTIONS_CACHE_URL'])
|
||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN'])
|
||||
Object.keys(process.env).forEach(function (key) {
|
||||
if (key.startsWith('ACTIONS_')) {
|
||||
core.info(`Exporting ${key}`);
|
||||
core.exportVariable(key, process.env[key]);
|
||||
}
|
||||
});
|
||||
|
||||
- name: Construct docker build cache args
|
||||
shell: bash
|
||||
@@ -32,7 +48,7 @@ runs:
|
||||
# which are set automatically when running on GitHub infra: https://docs.docker.com/build/cache/backends/gha/#synopsis
|
||||
|
||||
# Use cirrus cache host
|
||||
if [[ ${{ inputs.use-cirrus }} == 'true' ]]; then
|
||||
if [[ ${{ inputs.cache-provider }} == 'cirrus' ]]; then
|
||||
url_args="url=${CIRRUS_CACHE_HOST},url_v2=${CIRRUS_CACHE_HOST}"
|
||||
else
|
||||
url_args=""
|
||||
|
||||
@@ -17,7 +17,7 @@ runs:
|
||||
- name: Set cache hashes
|
||||
shell: bash
|
||||
run: |
|
||||
echo "DEPENDS_HASH=$(git ls-tree HEAD depends "ci/test/$FILE_ENV" | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
|
||||
echo "DEPENDS_HASH=$(git ls-tree HEAD depends "$FILE_ENV" | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
|
||||
echo "PREVIOUS_RELEASES_HASH=$(git ls-tree HEAD test/get_previous_releases.py | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
|
||||
|
||||
- name: Get container name
|
||||
|
||||
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@@ -33,15 +33,15 @@ jobs:
|
||||
name: 'determine runners'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
use-cirrus-runners: ${{ steps.runners.outputs.use-cirrus-runners }}
|
||||
provider: ${{ steps.runners.outputs.provider }}
|
||||
steps:
|
||||
- id: runners
|
||||
run: |
|
||||
if [[ "${REPO_USE_CIRRUS_RUNNERS}" == "${{ github.repository }}" ]]; then
|
||||
echo "use-cirrus-runners=true" >> "$GITHUB_OUTPUT"
|
||||
echo "provider=cirrus" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice title=Runner Selection::Using Cirrus Runners"
|
||||
else
|
||||
echo "use-cirrus-runners=false" >> "$GITHUB_OUTPUT"
|
||||
echo "provider=gha" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice title=Runner Selection::Using GitHub-hosted runners"
|
||||
fi
|
||||
|
||||
@@ -312,7 +312,7 @@ jobs:
|
||||
windows-cross:
|
||||
name: 'Linux->Windows cross, no tests'
|
||||
needs: runners
|
||||
runs-on: ${{ needs.runners.outputs.use-cirrus-runners == 'true' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-sm' || 'ubuntu-24.04' }}
|
||||
runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-sm' || 'ubuntu-24.04' }}
|
||||
if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
@@ -332,7 +332,7 @@ jobs:
|
||||
- name: Configure Docker
|
||||
uses: ./.github/actions/configure-docker
|
||||
with:
|
||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
||||
cache-provider: ${{ needs.runners.outputs.provider }}
|
||||
|
||||
- name: CI script
|
||||
run: ./ci/test_run_all.sh
|
||||
@@ -422,7 +422,7 @@ jobs:
|
||||
ci-matrix:
|
||||
name: ${{ matrix.name }}
|
||||
needs: runners
|
||||
runs-on: ${{ needs.runners.outputs.use-cirrus-runners == 'true' && matrix.cirrus-runner || matrix.fallback-runner }}
|
||||
runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && matrix.cirrus-runner || matrix.fallback-runner }}
|
||||
if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }}
|
||||
timeout-minutes: ${{ matrix.timeout-minutes }}
|
||||
|
||||
@@ -439,6 +439,7 @@ jobs:
|
||||
fallback-runner: 'ubuntu-24.04-arm'
|
||||
timeout-minutes: 120
|
||||
file-env: './ci/test/00_setup_env_arm.sh'
|
||||
provider: 'gha'
|
||||
|
||||
- name: 'ASan + LSan + UBSan + integer, no depends, USDT'
|
||||
cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' # has to match container in ci/test/00_setup_env_native_asan.sh for tracing tools
|
||||
@@ -513,7 +514,7 @@ jobs:
|
||||
- name: Configure Docker
|
||||
uses: ./.github/actions/configure-docker
|
||||
with:
|
||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
||||
cache-provider: ${{ matrix.provider || needs.runners.outputs.provider }}
|
||||
|
||||
- name: Enable bpfcc script
|
||||
if: ${{ env.CONTAINER_NAME == 'ci_native_asan' }}
|
||||
@@ -535,7 +536,7 @@ jobs:
|
||||
lint:
|
||||
name: 'lint'
|
||||
needs: runners
|
||||
runs-on: ${{ needs.runners.outputs.use-cirrus-runners == 'true' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xs' || 'ubuntu-24.04' }}
|
||||
runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xs' || 'ubuntu-24.04' }}
|
||||
if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
@@ -550,7 +551,7 @@ jobs:
|
||||
- name: Configure Docker
|
||||
uses: ./.github/actions/configure-docker
|
||||
with:
|
||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
||||
cache-provider: ${{ needs.runners.outputs.provider }}
|
||||
|
||||
- name: CI script
|
||||
run: |
|
||||
|
||||
@@ -28,11 +28,11 @@ get_directory_property(precious_variables CACHE_VARIABLES)
|
||||
#=============================
|
||||
set(CLIENT_NAME "Bitcoin Core")
|
||||
set(CLIENT_VERSION_MAJOR 30)
|
||||
set(CLIENT_VERSION_MINOR 0)
|
||||
set(CLIENT_VERSION_MINOR 2)
|
||||
set(CLIENT_VERSION_BUILD 0)
|
||||
set(CLIENT_VERSION_RC 1)
|
||||
set(CLIENT_VERSION_RC 0)
|
||||
set(CLIENT_VERSION_IS_RELEASE "true")
|
||||
set(COPYRIGHT_YEAR "2025")
|
||||
set(COPYRIGHT_YEAR "2026")
|
||||
|
||||
# During the enabling of the CXX and CXXOBJ languages, we modify
|
||||
# CMake's compiler/linker invocation strings by appending the content
|
||||
|
||||
4
COPYING
4
COPYING
@@ -1,7 +1,7 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (c) 2009-2025 Bitcoin Developers
|
||||
Copyright (c) 2009-2026 The Bitcoin Core developers
|
||||
Copyright (c) 2009-2026 Bitcoin Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -13,6 +13,7 @@ export CI_IMAGE_PLATFORM="linux/amd64"
|
||||
export PACKAGES="llvm clang g++-multilib"
|
||||
export DEP_OPTS="DEBUG=1 NO_IPC=1"
|
||||
export GOAL="install"
|
||||
export CI_LIMIT_STACK_SIZE=1
|
||||
export TEST_RUNNER_EXTRA="--v2transport --usecli"
|
||||
export BITCOIN_CONFIG="\
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
export LC_ALL=C.UTF-8
|
||||
|
||||
export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04"
|
||||
export APT_LLVM_V="21"
|
||||
LIBCXX_DIR="/cxx_build/"
|
||||
export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
|
||||
LIBCXX_FLAGS="-nostdinc++ -nostdlib++ -isystem ${LIBCXX_DIR}include/c++/v1 -L${LIBCXX_DIR}lib -Wl,-rpath,${LIBCXX_DIR}lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument"
|
||||
# -lstdc++ to resolve link issues due to upstream packaging
|
||||
LIBCXX_FLAGS="-nostdinc++ -nostdlib++ -isystem ${LIBCXX_DIR}include/c++/v1 -L${LIBCXX_DIR}lib -Wl,-rpath,${LIBCXX_DIR}lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument -lstdc++"
|
||||
export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
|
||||
|
||||
export CONTAINER_NAME="ci_native_fuzz_msan"
|
||||
export PACKAGES="clang-${APT_LLVM_V} llvm-${APT_LLVM_V} llvm-${APT_LLVM_V}-dev libclang-${APT_LLVM_V}-dev libclang-rt-${APT_LLVM_V}-dev"
|
||||
export DEP_OPTS="DEBUG=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||
export GOAL="all"
|
||||
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
|
||||
|
||||
@@ -8,7 +8,6 @@ export LC_ALL=C.UTF-8
|
||||
|
||||
export CONTAINER_NAME=ci_win64
|
||||
export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04" # Check that https://packages.ubuntu.com/noble/g++-mingw-w64-x86-64-posix (version 13.x, similar to guix) can cross-compile
|
||||
export CI_IMAGE_PLATFORM="linux/amd64"
|
||||
export HOST=x86_64-w64-mingw32
|
||||
export PACKAGES="g++-mingw-w64-x86-64-posix nsis"
|
||||
export RUN_UNIT_TESTS=false
|
||||
|
||||
@@ -56,25 +56,7 @@ if [ -n "$PIP_PACKAGES" ]; then
|
||||
fi
|
||||
|
||||
if [[ -n "${USE_INSTRUMENTED_LIBCPP}" ]]; then
|
||||
if [ -n "${APT_LLVM_V}" ]; then
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-$( clang --version | sed --silent 's@.*clang version \([0-9.]*\).*@\1@p' )" /llvm-project
|
||||
else
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.0" /llvm-project
|
||||
|
||||
cmake -G Ninja -B /clang_build/ \
|
||||
-DLLVM_ENABLE_PROJECTS="clang" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_TARGETS_TO_BUILD=Native \
|
||||
-DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \
|
||||
-S /llvm-project/llvm
|
||||
|
||||
ninja -C /clang_build/ "$MAKEJOBS"
|
||||
ninja -C /clang_build/ install-runtimes
|
||||
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /clang_build/bin/clang++ 100
|
||||
update-alternatives --install /usr/bin/clang clang /clang_build/bin/clang 100
|
||||
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /clang_build/bin/llvm-symbolizer 100
|
||||
fi
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.1" /llvm-project
|
||||
|
||||
cmake -G Ninja -B /cxx_build/ \
|
||||
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
|
||||
@@ -107,7 +89,7 @@ mkdir -p "${DEPENDS_DIR}/SDKs" "${DEPENDS_DIR}/sdk-sources"
|
||||
OSX_SDK_BASENAME="Xcode-${XCODE_VERSION}-${XCODE_BUILD_ID}-extracted-SDK-with-libcxx-headers"
|
||||
|
||||
if [ -n "$XCODE_VERSION" ] && [ ! -d "${DEPENDS_DIR}/SDKs/${OSX_SDK_BASENAME}" ]; then
|
||||
OSX_SDK_FILENAME="${OSX_SDK_BASENAME}.tar.gz"
|
||||
OSX_SDK_FILENAME="${OSX_SDK_BASENAME}.tar"
|
||||
OSX_SDK_PATH="${DEPENDS_DIR}/sdk-sources/${OSX_SDK_FILENAME}"
|
||||
if [ ! -f "$OSX_SDK_PATH" ]; then
|
||||
${CI_RETRY_EXE} curl --location --fail "${SDK_URL}/${OSX_SDK_FILENAME}" -o "$OSX_SDK_PATH"
|
||||
|
||||
@@ -29,7 +29,7 @@ function(add_boost_if_needed)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Boost 1.73.0 REQUIRED CONFIG)
|
||||
find_package(Boost 1.74.0 REQUIRED CONFIG)
|
||||
mark_as_advanced(Boost_INCLUDE_DIR boost_headers_DIR)
|
||||
# Workaround for a bug in NetBSD pkgsrc.
|
||||
# See: https://github.com/NetBSD/pkgsrc/issues/167.
|
||||
|
||||
@@ -36,6 +36,10 @@ if(USDT_INCLUDE_DIR)
|
||||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${USDT_INCLUDE_DIR})
|
||||
check_cxx_source_compiles("
|
||||
#if defined(__arm__)
|
||||
# define STAP_SDT_ARG_CONSTRAINT g
|
||||
#endif
|
||||
|
||||
// Setting SDT_USE_VARIADIC lets systemtap (sys/sdt.h) know that we want to use
|
||||
// the optional variadic macros to define tracepoints.
|
||||
#define SDT_USE_VARIADIC 1
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or https://opensource.org/license/mit/.
|
||||
|
||||
enable_language(C)
|
||||
|
||||
function(add_secp256k1 subdir)
|
||||
message("")
|
||||
message("Configuring secp256k1 subtree...")
|
||||
@@ -30,7 +32,6 @@ function(add_secp256k1 subdir)
|
||||
string(STRIP "${SECP256K1_APPEND_LDFLAGS} ${APPEND_LDFLAGS}" SECP256K1_APPEND_LDFLAGS)
|
||||
set(SECP256K1_APPEND_LDFLAGS ${SECP256K1_APPEND_LDFLAGS} CACHE STRING "" FORCE)
|
||||
# We want to build libsecp256k1 with the most tested RelWithDebInfo configuration.
|
||||
enable_language(C)
|
||||
foreach(config IN LISTS CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES)
|
||||
if(config STREQUAL "")
|
||||
continue()
|
||||
|
||||
@@ -5,7 +5,7 @@ Upstream-Contact: Satoshi Nakamoto <satoshin@gmx.com>
|
||||
Source: https://github.com/bitcoin/bitcoin
|
||||
|
||||
Files: *
|
||||
Copyright: 2009-2025, Bitcoin Core Developers
|
||||
Copyright: 2009-2026, Bitcoin Core Developers
|
||||
License: Expat
|
||||
Comment: The Bitcoin Core Developers encompasses all contributors to the
|
||||
project, listed in the release notes or the git log.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -59,10 +60,11 @@ for relpath in BINARIES:
|
||||
print(f'{abspath} not found or not an executable', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
# take first line (which must contain version)
|
||||
verstr = r.stdout.splitlines()[0]
|
||||
# last word of line is the actual version e.g. v22.99.0-5c6b3d5b3508
|
||||
verstr = verstr.split()[-1]
|
||||
assert verstr.startswith('v')
|
||||
output = r.stdout.splitlines()[0]
|
||||
# find the version e.g. v30.99.0-ce771726f3e7
|
||||
search = re.search(r"v[0-9]\S+", output)
|
||||
assert search
|
||||
verstr = search.group(0)
|
||||
# remaining lines are copyright
|
||||
copyright = r.stdout.split('\n')[1:]
|
||||
assert copyright[0].startswith('Copyright (C)')
|
||||
|
||||
@@ -37,7 +37,7 @@ You can then either point to the SDK using the `SDK_PATH` environment variable:
|
||||
|
||||
```sh
|
||||
# Extract the SDK tarball to /path/to/parent/dir/of/extracted/SDK/Xcode-<foo>-<bar>-extracted-SDK-with-libcxx-headers
|
||||
tar -C /path/to/parent/dir/of/extracted/SDK -xaf /path/to/Xcode-<foo>-<bar>-extracted-SDK-with-libcxx-headers.tar.gz
|
||||
tar -C /path/to/parent/dir/of/extracted/SDK -xaf /path/to/Xcode-<foo>-<bar>-extracted-SDK-with-libcxx-headers.tar
|
||||
|
||||
# Indicate where to locate the SDK tarball
|
||||
export SDK_PATH=/path/to/parent/dir/of/extracted/SDK
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
((gnu packages bash) #:select (bash-minimal))
|
||||
(gnu packages bison)
|
||||
((gnu packages certs) #:select (nss-certs))
|
||||
((gnu packages check) #:select (libfaketime))
|
||||
((gnu packages cmake) #:select (cmake-minimal))
|
||||
(gnu packages commencement)
|
||||
(gnu packages compression)
|
||||
@@ -209,7 +210,17 @@ and abstract ELF, PE and MachO formats.")
|
||||
(base32
|
||||
"1j47vwq4caxfv0xw68kw5yh00qcpbd56d7rq6c483ma3y7s96yyz"))))
|
||||
(build-system cmake-build-system)
|
||||
(inputs (list openssl))
|
||||
(arguments
|
||||
(list
|
||||
#:phases
|
||||
#~(modify-phases %standard-phases
|
||||
(replace 'check
|
||||
(lambda* (#:key tests? #:allow-other-keys)
|
||||
(if tests?
|
||||
(invoke "faketime" "-f" "@2025-01-01 00:00:00" ;; Tests fail after 2025.
|
||||
"ctest" "--output-on-failure" "--no-tests=error")
|
||||
(format #t "test suite not run~%")))))))
|
||||
(inputs (list libfaketime openssl))
|
||||
(home-page "https://github.com/mtrojnar/osslsigncode")
|
||||
(synopsis "Authenticode signing and timestamping tool")
|
||||
(description "osslsigncode is a small tool that implements part of the
|
||||
|
||||
@@ -46,8 +46,7 @@ MAX_VERSIONS = {
|
||||
|
||||
# Ignore symbols that are exported as part of every executable
|
||||
IGNORE_EXPORTS = {
|
||||
'environ', '_environ', '__environ', '_fini', '_init', 'stdin',
|
||||
'stdout', 'stderr',
|
||||
'stdin', 'stdout', 'stderr',
|
||||
}
|
||||
|
||||
# Expected linker-loader names can be found here:
|
||||
@@ -112,7 +111,6 @@ ELF_ALLOWED_LIBRARIES = {
|
||||
'libfontconfig.so.1', # font support
|
||||
'libfreetype.so.6', # font parsing
|
||||
'libdl.so.2', # programming interface to dynamic linker
|
||||
'libxcb-cursor.so.0',
|
||||
'libxcb-icccm.so.4',
|
||||
'libxcb-image.so.0',
|
||||
'libxcb-shm.so.0',
|
||||
|
||||
@@ -44,15 +44,15 @@ xip -x Xcode_15.xip
|
||||
|
||||
### Step 2: Generating the SDK tarball from `Xcode.app`
|
||||
|
||||
To generate the SDK, run the script [`gen-sdk`](./gen-sdk) with the
|
||||
To generate the SDK, run the script [`gen-sdk.py`](./gen-sdk.py) with the
|
||||
path to `Xcode.app` (extracted in the previous stage) as the first argument.
|
||||
|
||||
```bash
|
||||
./contrib/macdeploy/gen-sdk '/path/to/Xcode.app'
|
||||
./contrib/macdeploy/gen-sdk.py '/path/to/Xcode.app'
|
||||
```
|
||||
|
||||
The generated archive should be: `Xcode-15.0-15A240d-extracted-SDK-with-libcxx-headers.tar.gz`.
|
||||
The `sha256sum` should be `c0c2e7bb92c1fee0c4e9f3a485e4530786732d6c6dd9e9f418c282aa6892f55d`.
|
||||
The generated archive should be: `Xcode-15.0-15A240d-extracted-SDK-with-libcxx-headers.tar`.
|
||||
The `sha256sum` should be `95b00dc41fa090747dc0a7907a5031a2fcb2d7f95c9584ba6bccdb99b6e3d498`.
|
||||
|
||||
## Deterministic macOS App Notes
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
import argparse
|
||||
import plistlib
|
||||
import pathlib
|
||||
import sys
|
||||
import tarfile
|
||||
import gzip
|
||||
import os
|
||||
import contextlib
|
||||
|
||||
@@ -22,12 +20,12 @@ def run():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
|
||||
|
||||
parser.add_argument('xcode_app', metavar='XCODEAPP', nargs=1)
|
||||
parser.add_argument("-o", metavar='OUTSDKTGZ', nargs=1, dest='out_sdktgz', required=False)
|
||||
parser.add_argument('xcode_app', metavar='XCODEAPP', type=pathlib.Path)
|
||||
parser.add_argument("-o", metavar='OUTSDKTAR', dest='out_sdkt', type=pathlib.Path, required=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
xcode_app = pathlib.Path(args.xcode_app[0]).resolve()
|
||||
xcode_app = args.xcode_app.resolve()
|
||||
assert xcode_app.is_dir(), "The supplied Xcode.app path '{}' either does not exist or is not a directory".format(xcode_app)
|
||||
|
||||
xcode_app_plist = xcode_app.joinpath("Contents/version.plist")
|
||||
@@ -47,11 +45,7 @@ def run():
|
||||
|
||||
out_name = "Xcode-{xcode_version}-{xcode_build_id}-extracted-SDK-with-libcxx-headers".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id)
|
||||
|
||||
if args.out_sdktgz:
|
||||
out_sdktgz_path = pathlib.Path(args.out_sdktgz_path)
|
||||
else:
|
||||
# Construct our own out_sdktgz if not specified on the command line
|
||||
out_sdktgz_path = pathlib.Path("./{}.tar.gz".format(out_name))
|
||||
out_sdkt_path = args.out_sdkt or pathlib.Path("./{}.tar".format(out_name))
|
||||
|
||||
def tarfp_add_with_base_change(tarfp, dir_to_add, alt_base_dir):
|
||||
"""Add all files in dir_to_add to tarfp, but prepend alt_base_dir to the files'
|
||||
@@ -68,6 +62,8 @@ def run():
|
||||
|
||||
"""
|
||||
def change_tarinfo_base(tarinfo):
|
||||
if tarinfo.name and tarinfo.name.endswith((".swiftmodule", ".modulemap")):
|
||||
return None
|
||||
if tarinfo.name and tarinfo.name.startswith("./"):
|
||||
tarinfo.name = str(pathlib.Path(alt_base_dir, tarinfo.name))
|
||||
if tarinfo.linkname and tarinfo.linkname.startswith("./"):
|
||||
@@ -81,16 +77,17 @@ def run():
|
||||
return tarinfo
|
||||
with cd(dir_to_add):
|
||||
# recursion already adds entries in sorted order
|
||||
tarfp.add(".", recursive=True, filter=change_tarinfo_base)
|
||||
tarfp.add("./usr/include", recursive=True, filter=change_tarinfo_base)
|
||||
tarfp.add("./usr/lib", recursive=True, filter=change_tarinfo_base)
|
||||
tarfp.add("./System/Library/Frameworks", recursive=True, filter=change_tarinfo_base)
|
||||
|
||||
print("Creating output .tar.gz file...")
|
||||
with out_sdktgz_path.open("wb") as fp:
|
||||
with gzip.GzipFile(fileobj=fp, mode='wb', compresslevel=9, mtime=0) as gzf:
|
||||
with tarfile.open(mode="w", fileobj=gzf, format=tarfile.GNU_FORMAT) as tarfp:
|
||||
print("Adding MacOSX SDK {} files...".format(sdk_version))
|
||||
tarfp_add_with_base_change(tarfp, sdk_dir, out_name)
|
||||
print("Done! Find the resulting gzipped tarball at:")
|
||||
print(out_sdktgz_path.resolve())
|
||||
print("Creating output .tar file...")
|
||||
with out_sdkt_path.open("wb") as fp:
|
||||
with tarfile.open(mode="w", fileobj=fp, format=tarfile.PAX_FORMAT) as tarfp:
|
||||
print("Adding MacOSX SDK {} files...".format(sdk_version))
|
||||
tarfp_add_with_base_change(tarfp, sdk_dir, out_name)
|
||||
print("Done! Find the resulting tarball at:")
|
||||
print(out_sdkt_path.resolve())
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -466,18 +466,18 @@ if config.translations_dir:
|
||||
sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n")
|
||||
sys.exit(1)
|
||||
|
||||
print("+ Adding Qt translations +")
|
||||
print("+ Adding Qt translations +")
|
||||
|
||||
translations = Path(config.translations_dir[0])
|
||||
translations = Path(config.translations_dir[0])
|
||||
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
|
||||
# ------------------------------------------------
|
||||
|
||||
|
||||
@@ -10,14 +10,13 @@ to addrman with).
|
||||
|
||||
Update `MIN_BLOCKS` in `makeseeds.py` and the `-m`/`--minblocks` arguments below, as needed.
|
||||
|
||||
The seeds compiled into the release are created from sipa's, achow101's and luke-jr's
|
||||
The seeds compiled into the release are created from sipa's and achow101's
|
||||
DNS seed, virtu's crawler, and asmap community AS map data. Run the following commands
|
||||
from the `/contrib/seeds` directory:
|
||||
|
||||
```
|
||||
curl https://bitcoin.sipa.be/seeds.txt.gz | gzip -dc > seeds_main.txt
|
||||
curl https://21.ninja/seeds.txt.gz | gzip -dc >> seeds_main.txt
|
||||
curl https://luke.dashjr.org/programs/bitcoin/files/charts/seeds.txt >> seeds_main.txt
|
||||
curl https://mainnet.achownodes.xyz/seeds.txt.gz | gzip -dc >> seeds_main.txt
|
||||
curl https://signet.achownodes.xyz/seeds.txt.gz | gzip -dc > seeds_signet.txt
|
||||
curl https://testnet.achownodes.xyz/seeds.txt.gz | gzip -dc > seeds_test.txt
|
||||
|
||||
@@ -38,7 +38,7 @@ endef
|
||||
define fetch_file
|
||||
( test -f $$($(1)_source_dir)/$(4) || \
|
||||
( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5))))
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(4),$(4),$(5))))
|
||||
endef
|
||||
|
||||
# Shell script to create a source tarball in $(1)_source from local directory
|
||||
|
||||
@@ -6,7 +6,7 @@ $(package)_sha256_hash=0e9c5446dc6f3beb8af6ebfcc9e27bcc6da6fe2860f7fc07b99144dfa
|
||||
$(package)_dependencies=libxcb libxcb_util_render libxcb_util_image
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts = --disable-static
|
||||
$(package)_config_opts = --disable-shared
|
||||
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
|
||||
endef
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ $(package)_patches += qtbase_avoid_native_float16.patch
|
||||
$(package)_patches += qtbase_avoid_qmain.patch
|
||||
$(package)_patches += qtbase_platformsupport.patch
|
||||
$(package)_patches += qtbase_plugins_cocoa.patch
|
||||
$(package)_patches += qtbase_plugins_windows11style.patch
|
||||
$(package)_patches += qtbase_skip_tools.patch
|
||||
$(package)_patches += rcc_hardcode_timestamp.patch
|
||||
$(package)_patches += qttools_skip_dependencies.patch
|
||||
@@ -261,6 +262,7 @@ define $(package)_preprocess_cmds
|
||||
patch -p1 -i $($(package)_patch_dir)/qtbase_avoid_qmain.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/qtbase_platformsupport.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/qtbase_plugins_cocoa.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/qtbase_plugins_windows11style.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/qtbase_skip_tools.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/rcc_hardcode_timestamp.patch
|
||||
endef
|
||||
|
||||
113
depends/patches/qt/qtbase_plugins_windows11style.patch
Normal file
113
depends/patches/qt/qtbase_plugins_windows11style.patch
Normal file
@@ -0,0 +1,113 @@
|
||||
QWindows11Style: Calculate Spinbox size based on CommonStyle size
|
||||
Use the calculation from Commonstyle and add the increased padding and
|
||||
horizontally layouted buttons to the horizontal size hint.
|
||||
|
||||
Fixes: QTBUG-130288
|
||||
Change-Id: I7932b782e7873a0178091a51379f17453eb585fd
|
||||
|
||||
Upstream commits:
|
||||
- Qt 6.8.1: 9107817eaceaacc968dbc767c24594566d637b8c
|
||||
- Qt 6.9.0: 96d46cad43517adefa2eb7cb8819a0b2cc9241e6
|
||||
|
||||
--- a/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp
|
||||
+++ b/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp
|
||||
@@ -2048,39 +2048,22 @@ QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *o
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
+#if QT_CONFIG(spinbox)
|
||||
case QStyle::CT_SpinBox: {
|
||||
if (const auto *spinBoxOpt = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
|
||||
// Add button + frame widths
|
||||
- int width = 0;
|
||||
-
|
||||
- if (const QDateTimeEdit *spinBox = qobject_cast<const QDateTimeEdit *>(widget)) {
|
||||
- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->minimumDateTime().toString(spinBox->displayFormat()));
|
||||
- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->maximumDateTime().toString(spinBox->displayFormat()));
|
||||
- width = qMax(textSizeMin.width(),textSizeMax.width());
|
||||
- } else if (const QSpinBox *spinBox = qobject_cast<const QSpinBox *>(widget)) {
|
||||
- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->minimum()));
|
||||
- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->maximum()));
|
||||
- width = qMax(textSizeMin.width(),textSizeMax.width());
|
||||
- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->prefix()).width();
|
||||
- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->suffix()).width();
|
||||
-
|
||||
- } else if (const QDoubleSpinBox *spinBox = qobject_cast<const QDoubleSpinBox *>(widget)) {
|
||||
- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->minimum()));
|
||||
- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->maximum()));
|
||||
- width = qMax(textSizeMin.width(),textSizeMax.width());
|
||||
- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->prefix()).width();
|
||||
- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->suffix()).width();
|
||||
- }
|
||||
const qreal dpi = QStyleHelper::dpi(option);
|
||||
const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons);
|
||||
- const int buttonWidth = hasButtons ? 2 * qRound(QStyleHelper::dpiScaled(16, dpi)) : 0;
|
||||
+ const int margins = 8;
|
||||
+ const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0;
|
||||
const int frameWidth = spinBoxOpt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth,
|
||||
spinBoxOpt, widget) : 0;
|
||||
- contentSize.setWidth(2 * 12 + width);
|
||||
- contentSize += QSize(buttonWidth + 2 * frameWidth, 2 * frameWidth);
|
||||
+
|
||||
+ contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
+#endif
|
||||
default:
|
||||
contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
|
||||
break;
|
||||
|
||||
|
||||
Windows11Style: don't set minimum width for QAbstractSpinBox
|
||||
|
||||
There is no need to set a minimum width for QAbstractSpinBox in
|
||||
QWindows11Style::polish() as this might override the user preferences.
|
||||
Also the minimum size handling is now properly done within
|
||||
sizeFromContents().
|
||||
|
||||
Change-Id: Ibc1fd7a6f862fc85e3739025b9de581aa235d74c
|
||||
|
||||
Upstream commits:
|
||||
- Qt 6.8.3: f86da3d3f853adb1a5b823c1cc7be6db4a0265f3
|
||||
- Qt 6.9.0: b93a8dfdfe6900cb542fdc587dd2682007a6ac53
|
||||
- Qt 6.10.0: 2ec4c28470de115c16944653a5d4f6209452d56c
|
||||
|
||||
--- a/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp
|
||||
+++ b/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp
|
||||
@@ -29,7 +29,6 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
const static int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners
|
||||
const static int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners
|
||||
-constexpr QLatin1StringView originalWidthProperty("_q_windows11_style_original_width");
|
||||
|
||||
enum WINUI3Color {
|
||||
subtleHighlightColor, //Subtle highlight based on alpha used for hovered elements
|
||||
@@ -2140,13 +2139,6 @@ void QWindows11Style::polish(QWidget* widget)
|
||||
pal.setColor(QPalette::ButtonText, pal.text().color());
|
||||
pal.setColor(QPalette::BrightText, pal.text().color());
|
||||
widget->setPalette(pal);
|
||||
- } else if (widget->inherits("QAbstractSpinBox")) {
|
||||
- const int minWidth = 2 * 24 + 40;
|
||||
- const int originalWidth = widget->size().width();
|
||||
- if (originalWidth < minWidth) {
|
||||
- widget->resize(minWidth, widget->size().height());
|
||||
- widget->setProperty(originalWidthProperty.constData(), originalWidth);
|
||||
- }
|
||||
} else if (widget->inherits("QAbstractButton") || widget->inherits("QToolButton")) {
|
||||
widget->setAutoFillBackground(false);
|
||||
auto pal = widget->palette();
|
||||
@@ -2191,13 +2183,6 @@ void QWindows11Style::unpolish(QWidget *widget)
|
||||
scrollarea->viewport()->setPalette(pal);
|
||||
scrollarea->viewport()->setProperty("_q_original_background_palette", QVariant());
|
||||
}
|
||||
- if (widget->inherits("QAbstractSpinBox")) {
|
||||
- const QVariant originalWidth = widget->property(originalWidthProperty.constData());
|
||||
- if (originalWidth.isValid()) {
|
||||
- widget->resize(originalWidth.toInt(), widget->size().height());
|
||||
- widget->setProperty(originalWidthProperty.constData(), QVariant());
|
||||
- }
|
||||
- }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -101,5 +101,5 @@ cmake -B build -DENABLE_WALLET=OFF
|
||||
|
||||
```bash
|
||||
cmake --build build # Append "-j N" for N parallel jobs.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests.
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ cmake -B build
|
||||
SQLite is required for the wallet:
|
||||
|
||||
```bash
|
||||
pkgin sqlite3
|
||||
pkgin install sqlite3
|
||||
```
|
||||
|
||||
To build Bitcoin Core without the wallet, use `-DENABLE_WALLET=OFF`.
|
||||
@@ -42,7 +42,7 @@ To build Bitcoin Core without the wallet, use `-DENABLE_WALLET=OFF`.
|
||||
Cap'n Proto is needed for IPC functionality (see [multiprocess.md](multiprocess.md)):
|
||||
|
||||
```bash
|
||||
pkgin capnproto
|
||||
pkgin install capnproto
|
||||
```
|
||||
|
||||
Compile with `-DENABLE_IPC=OFF` if you do not need IPC functionality.
|
||||
@@ -84,7 +84,7 @@ Otherwise, if you don't need QR encoding support, use the `-DWITH_QRENCODE=OFF`
|
||||
|
||||
Bitcoin Core can provide notifications via ZeroMQ. If the package is installed, support will be compiled in.
|
||||
```bash
|
||||
pkgin zeromq
|
||||
pkgin install zeromq
|
||||
```
|
||||
|
||||
#### Test Suite Dependencies
|
||||
@@ -115,5 +115,5 @@ Build and run the tests:
|
||||
|
||||
```bash
|
||||
cmake --build build # Append "-j N" for N parallel jobs.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests.
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OpenBSD Build Guide
|
||||
|
||||
**Updated for OpenBSD [7.6](https://www.openbsd.org/76.html)**
|
||||
**Updated for OpenBSD [7.8](https://www.openbsd.org/78.html)**
|
||||
|
||||
This guide describes how to build bitcoind, command-line utilities, and GUI on OpenBSD.
|
||||
|
||||
@@ -21,8 +21,11 @@ pkg_add sqlite3
|
||||
|
||||
To build Bitcoin Core without the wallet, use `-DENABLE_WALLET=OFF`.
|
||||
|
||||
Cap'n Proto is needed for IPC functionality (see [multiprocess.md](multiprocess.md))
|
||||
and can be built from source: https://capnproto.org/install.html
|
||||
Cap'n Proto is needed for IPC functionality (see [multiprocess.md](multiprocess.md)):
|
||||
|
||||
```bash
|
||||
pkg_add capnproto
|
||||
```
|
||||
|
||||
Compile with `-DENABLE_IPC=OFF` if you do not need IPC functionality.
|
||||
|
||||
@@ -93,7 +96,7 @@ Run `cmake -B build -LH` to see the full list of available options.
|
||||
|
||||
```bash
|
||||
cmake --build build # Append "-j N" for N parallel jobs.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests.
|
||||
```
|
||||
|
||||
## Resource limits
|
||||
|
||||
@@ -170,7 +170,7 @@ Run the following in your terminal to compile Bitcoin Core:
|
||||
|
||||
``` bash
|
||||
cmake --build build # Append "-j N" here for N parallel jobs.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build # Append "-j N" for N parallel tests.
|
||||
```
|
||||
|
||||
### 3. Deploy (optional)
|
||||
|
||||
@@ -81,8 +81,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
||||
|
||||
sudo apt-get install qt6-base-dev qt6-tools-dev qt6-l10n-tools qt6-tools-dev-tools libgl-dev
|
||||
|
||||
For Qt 6.5 and later, the `libxcb-cursor0` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo apt install qt6-wayland
|
||||
@@ -133,8 +131,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
||||
|
||||
sudo dnf install qt6-qtbase-devel qt6-qttools-devel
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo dnf install qt6-qtwayland
|
||||
@@ -182,8 +178,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
||||
|
||||
apk add qt6-qtbase-dev qt6-qttools-dev
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
The GUI will be able to encode addresses in QR codes unless this feature is explicitly disabled. To install libqrencode, run:
|
||||
|
||||
apk add libqrencode-dev
|
||||
|
||||
@@ -55,7 +55,7 @@ In the following instructions, the "Debug" configuration can be specified instea
|
||||
```
|
||||
cmake -B build --preset vs2022-static # It might take a while if the vcpkg binary cache is unpopulated or invalidated.
|
||||
cmake --build build --config Release # Append "-j N" for N parallel jobs.
|
||||
ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests.
|
||||
cmake --install build --config Release # Optional.
|
||||
```
|
||||
|
||||
@@ -64,7 +64,7 @@ cmake --install build --config Release # Optional.
|
||||
```
|
||||
cmake -B build --preset vs2022 -DBUILD_GUI=OFF # It might take a while if the vcpkg binary cache is unpopulated or invalidated.
|
||||
cmake --build build --config Release # Append "-j N" for N parallel jobs.
|
||||
ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available.
|
||||
ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests.
|
||||
```
|
||||
|
||||
### 6. vcpkg-specific Issues and Workarounds
|
||||
|
||||
@@ -19,7 +19,7 @@ Bitcoin Core requires one of the following compilers.
|
||||
|
||||
| Dependency | Releases | Minimum required |
|
||||
| --- | --- | --- |
|
||||
| [Boost](../depends/packages/boost.mk) | [link](https://www.boost.org/users/download/) | [1.73.0](https://github.com/bitcoin/bitcoin/pull/29066) |
|
||||
| [Boost](../depends/packages/boost.mk) | [link](https://www.boost.org/users/download/) | [1.74.0](https://github.com/bitcoin/bitcoin/pull/34107) |
|
||||
| CMake | [link](https://cmake.org/) | [3.22](https://github.com/bitcoin/bitcoin/pull/30454) |
|
||||
| [libevent](../depends/packages/libevent.mk) | [link](https://github.com/libevent/libevent/releases) | [2.1.8](https://github.com/bitcoin/bitcoin/pull/24681) |
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
| *libbitcoin_crypto* | Hardware-optimized functions for data encryption, hashing, message authentication, and key derivation. |
|
||||
| *libbitcoin_kernel* | Consensus engine and support library used for validation by *libbitcoin_node*. |
|
||||
| *libbitcoinqt* | GUI functionality used by *bitcoin-qt* and *bitcoin-gui* executables. |
|
||||
| *libbitcoin_ipc* | IPC functionality used by *bitcoin-node*, *bitcoin-wallet*, *bitcoin-gui* executables to communicate when [`-DENABLE_IPC=ON`](multiprocess.md) is used. |
|
||||
| *libbitcoin_ipc* | IPC functionality used by *bitcoin-node* and *bitcoin-gui* executables to communicate when [`-DENABLE_IPC=ON`](multiprocess.md) is used. |
|
||||
| *libbitcoin_node* | P2P and RPC server functionality used by *bitcoind* and *bitcoin-qt* executables. |
|
||||
| *libbitcoin_util* | Home for common functionality shared by different executables and libraries. Similar to *libbitcoin_common*, but lower-level (see [Dependencies](#dependencies)). |
|
||||
| *libbitcoin_wallet* | Wallet functionality used by *bitcoind* and *bitcoin-wallet* executables. |
|
||||
|
||||
@@ -79,7 +79,7 @@ of the test. Just make sure to use double-dash to distinguish them from the
|
||||
fuzzer's own arguments:
|
||||
|
||||
```sh
|
||||
$ FUZZ=address_deserialize_v2 build_fuzz/bin/fuzz -runs=1 fuzz_corpora/address_deserialize_v2 --checkaddrman=5 --printtoconsole=1
|
||||
$ FUZZ=address_deserialize build_fuzz/bin/fuzz -runs=1 fuzz_corpora/address_deserialize --checkaddrman=5 --printtoconsole=1
|
||||
```
|
||||
|
||||
## Fuzzing corpora
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-CLI "1" "September 2025" "bitcoin-cli v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN-CLI "1" "January 2026" "bitcoin-cli v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-cli \- manual page for bitcoin-cli v30.0.0rc1
|
||||
bitcoin-cli \- manual page for bitcoin-cli v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR]
|
||||
@@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v30.0.0rc1
|
||||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,help <command>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core RPC client version v30.0.0rc1
|
||||
Bitcoin Core RPC client version v30.2.0
|
||||
.PP
|
||||
The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server.
|
||||
.PP
|
||||
@@ -188,7 +188,7 @@ additional "outonly" (or "o") argument can be passed to see
|
||||
outbound peers only. Pass "help" (or "h") for detailed help
|
||||
documentation.
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-QT "1" "September 2025" "bitcoin-qt v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN-QT "1" "January 2026" "bitcoin-qt v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-qt \- manual page for bitcoin-qt v30.0.0rc1
|
||||
bitcoin-qt \- manual page for bitcoin-qt v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-qt
|
||||
[\fI\,options\/\fR] [\fI\,URI\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core version v30.0.0rc1
|
||||
Bitcoin Core version v30.2.0
|
||||
.PP
|
||||
The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core.
|
||||
.PP
|
||||
@@ -695,13 +695,13 @@ Equivalent bytes per sigop in transactions for relay and mining
|
||||
.HP
|
||||
\fB\-datacarrier\fR
|
||||
.IP
|
||||
(DEPRECATED) Relay and mine data carrier transactions (default: 1)
|
||||
Relay and mine data carrier transactions (default: 1)
|
||||
.HP
|
||||
\fB\-datacarriersize\fR
|
||||
.IP
|
||||
(DEPRECATED) Relay and mine transactions whose data\-carrying raw
|
||||
scriptPubKeys in aggregate are of this size or less, allowing
|
||||
multiple outputs (default: 100000)
|
||||
Relay and mine transactions whose data\-carrying raw scriptPubKeys in
|
||||
aggregate are of this size or less, allowing multiple outputs
|
||||
(default: 100000)
|
||||
.HP
|
||||
\fB\-minrelaytxfee=\fR<amt>
|
||||
.IP
|
||||
@@ -839,7 +839,7 @@ Reset all settings changed in the GUI
|
||||
.IP
|
||||
Show splash screen on startup (default: 1)
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-TX "1" "September 2025" "bitcoin-tx v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN-TX "1" "January 2026" "bitcoin-tx v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-tx \- manual page for bitcoin-tx v30.0.0rc1
|
||||
bitcoin-tx \- manual page for bitcoin-tx v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR]
|
||||
@@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v30.0.0rc1
|
||||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-tx utility version v30.0.0rc1
|
||||
Bitcoin Core bitcoin\-tx utility version v30.2.0
|
||||
.PP
|
||||
The bitcoin\-tx tool is used for creating and modifying bitcoin transactions.
|
||||
.PP
|
||||
@@ -146,7 +146,7 @@ set=NAME:JSON\-STRING
|
||||
.IP
|
||||
Set register NAME to given JSON\-STRING
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-UTIL "1" "September 2025" "bitcoin-util v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN-UTIL "1" "January 2026" "bitcoin-util v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-util \- manual page for bitcoin-util v30.0.0rc1
|
||||
bitcoin-util \- manual page for bitcoin-util v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-util
|
||||
[\fI\,options\/\fR] [\fI\,command\/\fR]
|
||||
@@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v30.0.0rc1
|
||||
.B bitcoin-util
|
||||
[\fI\,options\/\fR] \fI\,grind <hex-block-header>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-util utility version v30.0.0rc1
|
||||
Bitcoin Core bitcoin\-util utility version v30.2.0
|
||||
.PP
|
||||
The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below.
|
||||
.SH OPTIONS
|
||||
@@ -65,7 +65,7 @@ grind
|
||||
.IP
|
||||
Perform proof of work on hex header string
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-WALLET "1" "September 2025" "bitcoin-wallet v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN-WALLET "1" "January 2026" "bitcoin-wallet v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v30.0.0rc1
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-wallet
|
||||
[\fI\,options\/\fR] \fI\,<command>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-wallet utility version v30.0.0rc1
|
||||
Bitcoin Core bitcoin\-wallet utility version v30.2.0
|
||||
.PP
|
||||
bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.
|
||||
.PP
|
||||
@@ -100,7 +100,7 @@ info
|
||||
.IP
|
||||
Get wallet info
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN "1" "September 2025" "bitcoin v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIN "1" "January 2026" "bitcoin v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin \- manual page for bitcoin v30.0.0rc1
|
||||
bitcoin \- manual page for bitcoin v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin
|
||||
[\fI\,OPTIONS\/\fR] \fI\,COMMAND\/\fR...
|
||||
@@ -46,7 +46,7 @@ chainstate [ARGS] Run bitcoin kernel chainstate util, equivalent to running 'bit
|
||||
test [ARGS] Run unit tests, equivalent to running 'test_bitcoin [ARGS]'.
|
||||
test\-gui [ARGS] Run GUI unit tests, equivalent to running 'test_bitcoin\-qt [ARGS]'.
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIND "1" "September 2025" "bitcoind v30.0.0rc1" "User Commands"
|
||||
.TH BITCOIND "1" "January 2026" "bitcoind v30.2.0" "User Commands"
|
||||
.SH NAME
|
||||
bitcoind \- manual page for bitcoind v30.0.0rc1
|
||||
bitcoind \- manual page for bitcoind v30.2.0
|
||||
.SH SYNOPSIS
|
||||
.B bitcoind
|
||||
[\fI\,options\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core daemon version v30.0.0rc1
|
||||
Bitcoin Core daemon version v30.2.0 bitcoind
|
||||
.PP
|
||||
The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses.
|
||||
.PP
|
||||
@@ -695,13 +695,13 @@ Equivalent bytes per sigop in transactions for relay and mining
|
||||
.HP
|
||||
\fB\-datacarrier\fR
|
||||
.IP
|
||||
(DEPRECATED) Relay and mine data carrier transactions (default: 1)
|
||||
Relay and mine data carrier transactions (default: 1)
|
||||
.HP
|
||||
\fB\-datacarriersize\fR
|
||||
.IP
|
||||
(DEPRECATED) Relay and mine transactions whose data\-carrying raw
|
||||
scriptPubKeys in aggregate are of this size or less, allowing
|
||||
multiple outputs (default: 100000)
|
||||
Relay and mine transactions whose data\-carrying raw scriptPubKeys in
|
||||
aggregate are of this size or less, allowing multiple outputs
|
||||
(default: 100000)
|
||||
.HP
|
||||
\fB\-minrelaytxfee=\fR<amt>
|
||||
.IP
|
||||
@@ -817,7 +817,7 @@ subject to empty whitelists.
|
||||
.IP
|
||||
Accept command line and JSON\-RPC commands
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2009-2025 The Bitcoin Core developers
|
||||
Copyright (C) 2009-2026 The Bitcoin Core developers
|
||||
|
||||
Please contribute if you find Bitcoin Core useful. Visit
|
||||
<https://bitcoincore.org/> for further information about the software.
|
||||
|
||||
@@ -1 +1,91 @@
|
||||
See https://github.com/bitcoin-core/bitcoin-devwiki/wiki/v30.0-Release-Notes-Draft.
|
||||
v30.2 Release Notes
|
||||
===================
|
||||
|
||||
Bitcoin Core version v30.2 is now available from:
|
||||
|
||||
<https://bitcoincore.org/bin/bitcoin-core-30.2/>
|
||||
|
||||
This release includes new features, various bug fixes and performance
|
||||
improvements, as well as updated translations.
|
||||
|
||||
Please report bugs using the issue tracker at GitHub:
|
||||
|
||||
<https://github.com/bitcoin/bitcoin/issues>
|
||||
|
||||
To receive security and update notifications, please subscribe to:
|
||||
|
||||
<https://bitcoincore.org/en/list/announcements/join/>
|
||||
|
||||
How to Upgrade
|
||||
==============
|
||||
|
||||
If you are running an older version, shut it down. Wait until it has completely
|
||||
shut down (which might take a few minutes in some cases), then run the
|
||||
installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on macOS)
|
||||
or `bitcoind`/`bitcoin-qt` (on Linux).
|
||||
|
||||
Upgrading directly from a version of Bitcoin Core that has reached its EOL is
|
||||
possible, but it might take some time if the data directory needs to be migrated. Old
|
||||
wallet versions of Bitcoin Core are generally supported.
|
||||
|
||||
Compatibility
|
||||
==============
|
||||
|
||||
Bitcoin Core is supported and tested on operating systems using the
|
||||
Linux Kernel 3.17+, macOS 13+, and Windows 10+. Bitcoin
|
||||
Core should also work on most other Unix-like systems but is not as
|
||||
frequently tested on them. It is not recommended to use Bitcoin Core on
|
||||
unsupported systems.
|
||||
|
||||
Notable changes
|
||||
===============
|
||||
|
||||
### Wallet
|
||||
|
||||
- #34156 wallet: fix unnamed legacy wallet migration failure
|
||||
- #34215 wallettool: fix unnamed createfromdump failure walletsdir deletion
|
||||
- #34221 test: migration, avoid backup name mismatch in default_wallet_failure
|
||||
|
||||
### IPC
|
||||
|
||||
- #33511 init: Fix Ctrl-C shutdown hangs during wait calls
|
||||
|
||||
### Build
|
||||
|
||||
- #33950 guix: reduce allowed exported symbols
|
||||
- #34107 build: Update minimum required Boost version
|
||||
- #34227 guix: Fix osslsigncode tests
|
||||
|
||||
### Test
|
||||
|
||||
- #34137 test: Avoid hard time.sleep(1) in feature_init.py
|
||||
- #34226 wallet: test: Relative wallet failed migration cleanup
|
||||
|
||||
### Fuzz
|
||||
|
||||
- #34091 fuzz: doc: remove any mention to address_deserialize_v2
|
||||
|
||||
### Doc
|
||||
|
||||
- #34182 doc: Update OpenBSD Build Guide
|
||||
|
||||
### Misc
|
||||
|
||||
- #34174 doc: update copyright year to 2026
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Thanks to everyone who directly contributed to this release:
|
||||
|
||||
- Ava Chow
|
||||
- brunoerg
|
||||
- davidgumberg
|
||||
- fanquake
|
||||
- furszy
|
||||
- Hennadii Stepanov
|
||||
- MarcoFalke
|
||||
- Ryan Ofsky
|
||||
|
||||
As well as to everyone that helped with translations on
|
||||
[Transifex](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
|
||||
@@ -111,7 +111,7 @@ For the period during which the notes are being edited on the wiki, the version
|
||||
|
||||
Generate list of authors:
|
||||
|
||||
git log --format='- %aN' v(current version, e.g. 25.0)..v(new version, e.g. 25.1) | grep -v 'merge-script' | sort -fiu
|
||||
git log --format='- %aN' v(current version, e.g. 29.0)..v(new version, e.g. 30.0) | grep -v 'merge-script' | sort -fiu
|
||||
|
||||
### Setup and perform Guix builds
|
||||
|
||||
|
||||
@@ -576,12 +576,12 @@
|
||||
# (default: 20)
|
||||
#bytespersigop=1
|
||||
|
||||
# (DEPRECATED) Relay and mine data carrier transactions (default: 1)
|
||||
# Relay and mine data carrier transactions (default: 1)
|
||||
#datacarrier=1
|
||||
|
||||
# (DEPRECATED) Relay and mine transactions whose data-carrying raw
|
||||
# scriptPubKeys in aggregate are of this size or less, allowing
|
||||
# multiple outputs (default: 100000)
|
||||
# Relay and mine transactions whose data-carrying raw scriptPubKeys in
|
||||
# aggregate are of this size or less, allowing multiple outputs
|
||||
# (default: 100000)
|
||||
#datacarriersize=1
|
||||
|
||||
# Fees (in BTC/kvB) smaller than this are considered zero fee for
|
||||
|
||||
@@ -113,6 +113,19 @@ Section -post SEC0001
|
||||
WriteRegStr HKCR "@CLIENT_TARNAME@" "" "URL:Bitcoin"
|
||||
WriteRegStr HKCR "@CLIENT_TARNAME@\DefaultIcon" "" $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
|
||||
WriteRegStr HKCR "@CLIENT_TARNAME@\shell\open\command" "" '"$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" "%1"'
|
||||
|
||||
# Lingering since fb2b05b1259d3e69e6e675adfa30b429424c7625 which removed the suffix
|
||||
DeleteRegValue HKCU "${REGKEY} (64-bit)\Components" Main
|
||||
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name) (64-bit)"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name) (64-bit).lnk"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^Name) (64-bit).lnk"
|
||||
DeleteRegValue HKCU "${REGKEY} (64-bit)" StartMenuGroup
|
||||
DeleteRegValue HKCU "${REGKEY} (64-bit)" Path
|
||||
DeleteRegKey /IfEmpty HKCU "${REGKEY} (64-bit)\Components"
|
||||
DeleteRegKey /IfEmpty HKCU "${REGKEY} (64-bit)"
|
||||
|
||||
# Lingering since 77b2923f87131a407f7d4193c54db22375130403
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Bitcoin Core (testnet, 64-bit).lnk"
|
||||
SectionEnd
|
||||
|
||||
# Macro for selecting uninstaller sections
|
||||
|
||||
@@ -292,8 +292,8 @@ if(BUILD_BITCOIN_BIN)
|
||||
add_executable(bitcoin bitcoin.cpp)
|
||||
add_windows_resources(bitcoin bitcoin-res.rc)
|
||||
add_windows_application_manifest(bitcoin)
|
||||
target_link_libraries(bitcoin core_interface bitcoin_util)
|
||||
install_binary_component(bitcoin)
|
||||
target_link_libraries(bitcoin core_interface bitcoin_common bitcoin_util)
|
||||
install_binary_component(bitcoin HAS_MANPAGE)
|
||||
endif()
|
||||
|
||||
# Bitcoin Core bitcoind.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <common/args.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/exec.h>
|
||||
#include <util/strencodings.h>
|
||||
@@ -47,7 +48,7 @@ Run '%s help' to see additional commands (e.g. for testing and debugging).
|
||||
)";
|
||||
|
||||
struct CommandLine {
|
||||
bool use_multiprocess{false};
|
||||
std::optional<bool> use_multiprocess;
|
||||
bool show_version{false};
|
||||
bool show_help{false};
|
||||
std::string_view command;
|
||||
@@ -55,6 +56,7 @@ struct CommandLine {
|
||||
};
|
||||
|
||||
CommandLine ParseCommandLine(int argc, char* argv[]);
|
||||
bool UseMultiprocess(const CommandLine& cmd);
|
||||
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
@@ -78,9 +80,9 @@ int main(int argc, char* argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (cmd.command == "gui") {
|
||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-gui" : "bitcoin-qt");
|
||||
args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-gui" : "bitcoin-qt");
|
||||
} else if (cmd.command == "node") {
|
||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-node" : "bitcoind");
|
||||
args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-node" : "bitcoind");
|
||||
} else if (cmd.command == "rpc") {
|
||||
args.emplace_back("bitcoin-cli");
|
||||
// Since "bitcoin rpc" is a new interface that doesn't need to be
|
||||
@@ -143,6 +145,30 @@ CommandLine ParseCommandLine(int argc, char* argv[])
|
||||
return cmd;
|
||||
}
|
||||
|
||||
bool UseMultiprocess(const CommandLine& cmd)
|
||||
{
|
||||
// If -m or -M options were explicitly specified, there is no need to
|
||||
// further parse arguments to determine which to use.
|
||||
if (cmd.use_multiprocess) return *cmd.use_multiprocess;
|
||||
|
||||
ArgsManager args;
|
||||
args.SetDefaultFlags(ArgsManager::ALLOW_ANY);
|
||||
std::string error_message;
|
||||
auto argv{cmd.args};
|
||||
argv.insert(argv.begin(), nullptr);
|
||||
if (!args.ParseParameters(argv.size(), argv.data(), error_message)) {
|
||||
tfm::format(std::cerr, "Warning: failed to parse subcommand command line options: %s\n", error_message);
|
||||
}
|
||||
if (!args.ReadConfigFiles(error_message, true)) {
|
||||
tfm::format(std::cerr, "Warning: failed to parse subcommand config: %s\n", error_message);
|
||||
}
|
||||
args.SelectConfigNetwork(args.GetChainTypeString());
|
||||
|
||||
// If any -ipc* options are set these need to be processed by a
|
||||
// multiprocess-capable binary.
|
||||
return args.IsArgSet("-ipcbind") || args.IsArgSet("-ipcconnect") || args.IsArgSet("-ipcfd");
|
||||
}
|
||||
|
||||
//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
|
||||
//! using src, bin and libexec directory paths relative to this executable, where
|
||||
//! the path to this executable is specified in `wrapper_argv0`.
|
||||
|
||||
@@ -132,11 +132,16 @@ static bool ParseArgs(NodeContext& node, int argc, char* argv[])
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ProcessInitCommands(ArgsManager& args)
|
||||
static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args)
|
||||
{
|
||||
// Process help and version before taking care about datadir
|
||||
if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
|
||||
std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion() + "\n";
|
||||
std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion();
|
||||
if (const char* exe_name{init.exeName()}) {
|
||||
strUsage += " ";
|
||||
strUsage += exe_name;
|
||||
}
|
||||
strUsage += "\n";
|
||||
|
||||
if (args.GetBoolArg("-version", false)) {
|
||||
strUsage += FormatParagraph(LicenseInfo());
|
||||
@@ -277,7 +282,7 @@ MAIN_FUNCTION
|
||||
ArgsManager& args = *Assert(node.args);
|
||||
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
|
||||
// Process early info return commands such as -help or -version
|
||||
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
|
||||
if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS;
|
||||
|
||||
// Start application
|
||||
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
|
||||
|
||||
@@ -266,7 +266,13 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
|
||||
return search->second.m_flags;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return m_default_flags;
|
||||
}
|
||||
|
||||
void ArgsManager::SetDefaultFlags(std::optional<unsigned int> flags)
|
||||
{
|
||||
LOCK(cs_args);
|
||||
m_default_flags = flags;
|
||||
}
|
||||
|
||||
fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
|
||||
|
||||
@@ -137,6 +137,7 @@ protected:
|
||||
std::string m_network GUARDED_BY(cs_args);
|
||||
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
|
||||
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
|
||||
std::optional<unsigned int> m_default_flags GUARDED_BY(cs_args){};
|
||||
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
||||
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
||||
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
||||
@@ -375,10 +376,15 @@ protected:
|
||||
|
||||
/**
|
||||
* Return Flags for known arg.
|
||||
* Return nullopt for unknown arg.
|
||||
* Return default flags for unknown arg.
|
||||
*/
|
||||
std::optional<unsigned int> GetArgFlags(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Set default flags to return for an unknown arg.
|
||||
*/
|
||||
void SetDefaultFlags(std::optional<unsigned int>);
|
||||
|
||||
/**
|
||||
* Get settings file path, or return false if read-write settings were
|
||||
* disabled with -nosettings.
|
||||
|
||||
@@ -11,19 +11,25 @@
|
||||
#include <util/string.h>
|
||||
#include <util/time.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#include <compat/compat.h>
|
||||
#ifdef WIN32
|
||||
#include <codecvt>
|
||||
#include <compat/compat.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MALLOPT_ARENA_MAX
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -105,6 +111,22 @@ int GetNumCores()
|
||||
return std::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
std::optional<size_t> GetTotalRAM()
|
||||
{
|
||||
[[maybe_unused]] auto clamp{[](uint64_t v) { return size_t(std::min(v, uint64_t{std::numeric_limits<size_t>::max()})); }};
|
||||
#ifdef WIN32
|
||||
if (MEMORYSTATUSEX m{}; (m.dwLength = sizeof(m), GlobalMemoryStatusEx(&m))) return clamp(m.ullTotalPhys);
|
||||
#elif defined(__APPLE__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__illumos__) || \
|
||||
defined(__linux__)
|
||||
if (long p{sysconf(_SC_PHYS_PAGES)}, s{sysconf(_SC_PAGESIZE)}; p > 0 && s > 0) return clamp(1ULL * p * s);
|
||||
#endif
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Obtain the application startup time (used for uptime calculation)
|
||||
int64_t GetStartupTime()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// Application startup time (used for uptime calculation)
|
||||
@@ -29,4 +30,9 @@ void runCommand(const std::string& strCommand);
|
||||
*/
|
||||
int GetNumCores();
|
||||
|
||||
/**
|
||||
* Return the total RAM available on the current system, if detectable.
|
||||
*/
|
||||
std::optional<size_t> GetTotalRAM();
|
||||
|
||||
#endif // BITCOIN_COMMON_SYSTEM_H
|
||||
|
||||
13
src/init.cpp
13
src/init.cpp
@@ -215,8 +215,6 @@ void InitContext(NodeContext& node)
|
||||
node.shutdown_request = [&node] {
|
||||
assert(node.shutdown_signal);
|
||||
if (!(*node.shutdown_signal)()) return false;
|
||||
// Wake any threads that may be waiting for the tip to change.
|
||||
if (node.notifications) WITH_LOCK(node.notifications->m_tip_block_mutex, node.notifications->m_tip_block_cv.notify_all());
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -267,6 +265,8 @@ void Interrupt(NodeContext& node)
|
||||
#if HAVE_SYSTEM
|
||||
ShutdownNotify(*node.args);
|
||||
#endif
|
||||
// Wake any threads that may be waiting for the tip to change.
|
||||
if (node.notifications) WITH_LOCK(node.notifications->m_tip_block_mutex, node.notifications->m_tip_block_cv.notify_all());
|
||||
InterruptHTTPServer();
|
||||
InterruptHTTPRPC();
|
||||
InterruptRPC();
|
||||
@@ -658,9 +658,9 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
|
||||
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks<std::chrono::hours>(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
|
||||
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-datacarrier", strprintf("(DEPRECATED) Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-datacarriersize",
|
||||
strprintf("(DEPRECATED) Relay and mine transactions whose data-carrying raw scriptPubKeys in aggregate "
|
||||
strprintf("Relay and mine transactions whose data-carrying raw scriptPubKeys in aggregate "
|
||||
"are of this size or less, allowing multiple outputs (default: %u)",
|
||||
MAX_OP_RETURN_RELAY),
|
||||
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
@@ -903,10 +903,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
||||
InitWarning(_("Option '-checkpoints' is set but checkpoints were removed. This option has no effect."));
|
||||
}
|
||||
|
||||
if (args.IsArgSet("-datacarriersize") || args.IsArgSet("-datacarrier")) {
|
||||
InitWarning(_("Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version."));
|
||||
}
|
||||
|
||||
// We no longer limit the orphanage based on number of transactions but keep the option to warn users who still have it in their config.
|
||||
if (args.IsArgSet("-maxorphantx")) {
|
||||
InitWarning(_("Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration."));
|
||||
@@ -1733,6 +1729,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
// ********************************************************* Step 7: load block chain
|
||||
|
||||
// cache size calculations
|
||||
node::LogOversizedDbCache(args);
|
||||
const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
|
||||
|
||||
LogInfo("Cache configuration:");
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
// bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node
|
||||
// options and will start the node with those options.
|
||||
bool canListenIpc() override { return true; }
|
||||
const char* exeName() override { return EXE_NAME; }
|
||||
node::NodeContext m_node;
|
||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
||||
bool canListenIpc() override { return true; }
|
||||
const char* exeName() override { return EXE_NAME; }
|
||||
node::NodeContext& m_node;
|
||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace init {
|
||||
namespace {
|
||||
const char* EXE_NAME = "bitcoin-qt";
|
||||
|
||||
class BitcoinQtInit : public interfaces::Init
|
||||
{
|
||||
public:
|
||||
@@ -32,6 +34,7 @@ public:
|
||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||
}
|
||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||
const char* exeName() override { return EXE_NAME; }
|
||||
node::NodeContext m_node;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -18,6 +18,8 @@ using node::NodeContext;
|
||||
|
||||
namespace init {
|
||||
namespace {
|
||||
const char* EXE_NAME = "bitcoind";
|
||||
|
||||
class BitcoindInit : public interfaces::Init
|
||||
{
|
||||
public:
|
||||
@@ -34,6 +36,7 @@ public:
|
||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||
}
|
||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||
const char* exeName() override { return EXE_NAME; }
|
||||
NodeContext& m_node;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
|
||||
virtual Ipc* ipc() { return nullptr; }
|
||||
virtual bool canListenIpc() { return false; }
|
||||
virtual const char* exeName() { return nullptr; }
|
||||
};
|
||||
|
||||
//! Return implementation of Init interface for the node process. If the argv
|
||||
|
||||
@@ -72,6 +72,11 @@ public:
|
||||
* the tip is more than 20 minutes old.
|
||||
*/
|
||||
virtual std::unique_ptr<BlockTemplate> waitNext(const node::BlockWaitOptions options = {}) = 0;
|
||||
|
||||
/**
|
||||
* Interrupts the current wait for the next block template.
|
||||
*/
|
||||
virtual void interruptWait() = 0;
|
||||
};
|
||||
|
||||
//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)
|
||||
|
||||
@@ -33,6 +33,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
|
||||
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
|
||||
submitSolution @9 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool);
|
||||
waitNext @10 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate);
|
||||
interruptWait @11() -> ();
|
||||
}
|
||||
|
||||
struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {
|
||||
|
||||
@@ -30,10 +30,36 @@
|
||||
namespace ipc {
|
||||
namespace capnp {
|
||||
namespace {
|
||||
void IpcLogFn(bool raise, std::string message)
|
||||
|
||||
BCLog::Level ConvertIPCLogLevel(mp::Log level)
|
||||
{
|
||||
LogDebug(BCLog::IPC, "%s\n", message);
|
||||
if (raise) throw Exception(message);
|
||||
switch (level) {
|
||||
case mp::Log::Trace: return BCLog::Level::Trace;
|
||||
case mp::Log::Debug: return BCLog::Level::Debug;
|
||||
case mp::Log::Info: return BCLog::Level::Info;
|
||||
case mp::Log::Warning: return BCLog::Level::Warning;
|
||||
case mp::Log::Error: return BCLog::Level::Error;
|
||||
case mp::Log::Raise: return BCLog::Level::Error;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
||||
// Be conservative and assume that if MP ever adds a new log level, it
|
||||
// should only be shown at our most verbose level.
|
||||
return BCLog::Level::Trace;
|
||||
}
|
||||
|
||||
mp::Log GetRequestedIPCLogLevel()
|
||||
{
|
||||
if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Trace)) return mp::Log::Trace;
|
||||
if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Debug)) return mp::Log::Debug;
|
||||
|
||||
// Info, Warning, and Error are logged unconditionally
|
||||
return mp::Log::Info;
|
||||
}
|
||||
|
||||
void IpcLogFn(mp::LogMessage message)
|
||||
{
|
||||
LogPrintLevel(BCLog::IPC, ConvertIPCLogLevel(message.level), "%s\n", message.message);
|
||||
if (message.level == mp::Log::Raise) throw Exception(message.message);
|
||||
}
|
||||
|
||||
class CapnpProtocol : public Protocol
|
||||
@@ -62,7 +88,11 @@ public:
|
||||
{
|
||||
assert(!m_loop);
|
||||
mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
|
||||
m_loop.emplace(exe_name, &IpcLogFn, &m_context);
|
||||
mp::LogOptions opts = {
|
||||
.log_fn = IpcLogFn,
|
||||
.log_level = GetRequestedIPCLogLevel()
|
||||
};
|
||||
m_loop.emplace(exe_name, std::move(opts), &m_context);
|
||||
if (ready_fn) ready_fn();
|
||||
mp::ServeStream<messages::Init>(*m_loop, fd, init);
|
||||
m_parent_connection = &m_loop->m_incoming_connections.back();
|
||||
@@ -90,7 +120,11 @@ public:
|
||||
std::promise<void> promise;
|
||||
m_loop_thread = std::thread([&] {
|
||||
util::ThreadRename("capnp-loop");
|
||||
m_loop.emplace(exe_name, &IpcLogFn, &m_context);
|
||||
mp::LogOptions opts = {
|
||||
.log_fn = IpcLogFn,
|
||||
.log_level = GetRequestedIPCLogLevel()
|
||||
};
|
||||
m_loop.emplace(exe_name, std::move(opts), &m_context);
|
||||
m_loop_ref.emplace(*m_loop);
|
||||
promise.set_value();
|
||||
m_loop->loop();
|
||||
|
||||
5
src/ipc/libmultiprocess/.gitignore
vendored
Normal file
5
src/ipc/libmultiprocess/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# CMake artifacts
|
||||
/*build*
|
||||
|
||||
# Git artifacts
|
||||
*.patch
|
||||
@@ -2,7 +2,7 @@ CI_DESC="CI job using LLVM-based libraries and tools (clang, libc++, clang-tidy,
|
||||
CI_DIR=build-llvm
|
||||
NIX_ARGS=(--arg enableLibcxx true)
|
||||
export CXX=clang++
|
||||
export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wthread-safety-analysis -Wno-unused-parameter"
|
||||
export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wthread-safety -Wno-unused-parameter"
|
||||
CMAKE_ARGS=(
|
||||
-G Ninja
|
||||
-DMP_ENABLE_CLANG_TIDY=ON
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CI_DESC="CI job using old Cap'n Proto version"
|
||||
CI_DESC="CI job using old Cap'n Proto and cmake versions"
|
||||
CI_DIR=build-olddeps
|
||||
export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-error=array-bounds"
|
||||
NIX_ARGS=(--argstr capnprotoVersion "0.7.1")
|
||||
NIX_ARGS=(--argstr capnprotoVersion "0.7.1" --argstr cmakeVersion "3.12.4")
|
||||
BUILD_ARGS=(-k)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
CI_DESC="CI job running ThreadSanitizer"
|
||||
CI_DIR=build-sanitize
|
||||
NIX_ARGS=(--arg enableLibcxx true --argstr libcxxSanitizers "Thread" --argstr capnprotoSanitizers "thread")
|
||||
export CXX=clang++
|
||||
export CXXFLAGS="-ggdb -Werror -Wall -Wextra -Wpedantic -Wthread-safety-analysis -Wno-unused-parameter -fsanitize=thread"
|
||||
export CXXFLAGS="-ggdb -Werror -Wall -Wextra -Wpedantic -Wthread-safety -Wno-unused-parameter -fsanitize=thread"
|
||||
CMAKE_ARGS=()
|
||||
BUILD_ARGS=(-k -j4)
|
||||
BUILD_TARGETS=(mptest)
|
||||
|
||||
@@ -17,6 +17,21 @@ fi
|
||||
|
||||
[ -n "${CI_CLEAN-}" ] && rm -rf "${CI_DIR}"
|
||||
|
||||
cmake -B "$CI_DIR" "${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}"
|
||||
cmake --build "$CI_DIR" -t "${BUILD_TARGETS[@]}" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}"
|
||||
ctest --test-dir "$CI_DIR" --output-on-failure
|
||||
cmake --version
|
||||
cmake_ver=$(cmake --version | awk '/version/{print $3; exit}')
|
||||
ver_ge() { [ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]; }
|
||||
|
||||
src_dir=$PWD
|
||||
mkdir -p "$CI_DIR"
|
||||
cd "$CI_DIR"
|
||||
cmake "$src_dir" "${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}"
|
||||
if ver_ge "$cmake_ver" "3.15"; then
|
||||
cmake --build . -t "${BUILD_TARGETS[@]}" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}"
|
||||
else
|
||||
# Older versions of cmake can only build one target at a time with --target,
|
||||
# and do not support -t shortcut
|
||||
for t in "${BUILD_TARGETS[@]}"; do
|
||||
cmake --build . --target "$t" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}"
|
||||
done
|
||||
fi
|
||||
ctest --output-on-failure
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
Given an interface description of an object with one or more methods, libmultiprocess generates:
|
||||
|
||||
* A C++ `ProxyClient` class with an implementation of each interface method that sends a request over a socket, waits for a response, and returns the result.
|
||||
* A C++ `ProxyServer` class that listens for requests over a socket and calls a wrapped C++ object implementing the same interface to actually execute the requests.
|
||||
* A C++ `ProxyClient` class template specialization with an implementation of each interface method that sends a request over a socket, waits for a response, and returns the result.
|
||||
* A C++ `ProxyServer` class template specialization that listens for requests over a socket and calls a wrapped C++ object implementing the same interface to actually execute the requests.
|
||||
|
||||
The function call ⇆ request translation supports input and output arguments, standard types like `unique_ptr`, `vector`, `map`, and `optional`, and bidirectional calls between processes through interface pointer and `std::function` arguments.
|
||||
|
||||
@@ -15,7 +15,7 @@ Libmultiprocess acts as a pure wrapper or layer over the underlying protocol. Cl
|
||||
|
||||
### Internals
|
||||
|
||||
The `ProxyClient` and `ProxyServer` generated classes are not directly exposed to the user, as described in [usage.md](usage.md). Instead, they wrap c++ interfaces and appear to the user as pointers to an interface. They are first instantiated when calling `ConnectStream` and `ServeStream` respectively for creating the `InitInterface`. These methods establish connections through sockets, internally creating `Connection` objects wrapping a `capnp::RpcSystem` configured for client and server mode respectively.
|
||||
The `ProxyClient` and `ProxyServer` generated classes are not directly exposed to the user, as described in [usage.md](usage.md). Instead, they wrap C++ interfaces and appear to the user as pointers to an interface. They are first instantiated when calling `ConnectStream` and `ServeStream` respectively for creating the `InitInterface`. These methods establish connections through sockets, internally creating `Connection` objects wrapping a `capnp::RpcSystem` configured for client and server mode respectively.
|
||||
|
||||
The `InitInterface` interface will typically have methods which return other interfaces, giving the connecting process the ability to call other functions in the serving process. Interfaces can also have methods accepting other interfaces as parameters, giving serving processes the ability to call back and invoke functions in connecting processes. Creating new interfaces does not create new connections, and typically many interface objects will share the same connection.
|
||||
|
||||
@@ -23,13 +23,13 @@ Both `ConnectStream` and `ServeStream` also require an instantiation of the `Eve
|
||||
|
||||
When a generated method on the `ProxyClient` is called, it calls `clientInvoke` with the capnp-translated types. `clientInvoke` creates a self-executing promise (`kj::TaskSet`) that drives the execution of the request and gives ownership of it to the `EventLoop`. `clientInvoke` blocks until a response is received, or until there is a call from the server that needs to run on the same client thread, using a `Waiter` object.
|
||||
|
||||
On the server side, the `capnp::RpcSystem` receives the capnp request and invokes the corresponding c++ method through the corresponding `ProxyServer` and the heavily templated `serverInvoke` triggering a `ServerCall`. Its return values from the actual c++ methods are copied into capnp responses by `ServerRet` and exceptions are caught and copied by `ServerExcept`. The two are connected through `ServerField`. The main method driving execution of a request is `PassField`, which is invoked through `ServerField`. Instantiated interfaces, or capabilities in capnp speak, are tracked and owned by the server's `capnp::RpcSystem`.
|
||||
On the server side, the `capnp::RpcSystem` receives the capnp request and invokes the corresponding C++ method through the corresponding `ProxyServer` and the heavily templated `serverInvoke` triggering a `ServerCall`. The return values from the actual C++ methods are copied into capnp responses by `ServerRet` and exceptions are caught and copied by `ServerExcept`. The two are connected through `ServerField`. The main method driving execution of a request is `PassField`, which is invoked through `ServerField`. Instantiated interfaces, or capabilities in capnp speak, are tracked and owned by the server's `capnp::RpcSystem`.
|
||||
|
||||
## Interface descriptions
|
||||
|
||||
As explained in the [usage](usage.md) document, interface descriptions need to be consumed both by the _libmultiprocess_ code generator, and by C++ code that calls and implements the interfaces. The C++ code only needs to know about C++ arguments and return types, while the code generator only needs to know about capnp arguments and return types, but both need to know class and method names, so the corresponding `.h` and `.capnp` source files contain some of the same information, and have to be kept in sync manually when methods or parameters change. Despite the redundancy, reconciling the interface definitions is designed to be _straightforward_ and _safe_. _Straightforward_ because there is no need to write manual serialization code or use awkward intermediate types like [`UniValue`](https://github.com/bitcoin/bitcoin/blob/master/src/univalue/include/univalue.h) instead of native types. _Safe_ because if there are any inconsistencies between API and data definitions (even minor ones like using a narrow int data type for a wider int API input), there are errors at build time instead of errors or bugs at runtime.
|
||||
|
||||
In the future, it would be possible to combine API and data definitions together using [C++ attributes](https://en.cppreference.com/w/cpp/language/attributes). To do this we would add attributes to the API definition files, and then generate the data definitions from the API definitions and attributes. I didn't take this approach mostly because it would be extra work, but also because until c++ standardizes reflection, this would require either hooking into compiler APIs like https://github.com/RosettaCommons/binder, or parsing c++ code manually like http://www.swig.org/.
|
||||
In the future, it would be possible to combine API and data definitions together using [C++ attributes](https://en.cppreference.com/w/cpp/language/attributes). To do this we would add attributes to the API definition files, and then generate the data definitions from the API definitions and attributes. I didn't take this approach mostly because it would be extra work, but also because until C++ standardizes reflection, this would require either hooking into compiler APIs like https://github.com/RosettaCommons/binder, or parsing C++ code manually like http://www.swig.org/.
|
||||
|
||||
## What is `kj`?
|
||||
|
||||
@@ -39,6 +39,6 @@ basis in this library to construct the event-loop necessary to service IPC reque
|
||||
|
||||
## Future directions
|
||||
|
||||
_libmultiprocess_ uses the [Cap'n Proto](https://capnproto.org) interface description language and protocol, but it could be extended or changed to use a different IDL/protocol like [gRPC](https://grpc.io). The nice thing about _Cap'n Proto_ compared to _gRPC_ and most other lower level protocols is that it allows interface pointers (_Services_ in gRPC parlance) to be passed as method arguments and return values, so object references and bidirectional requests work out of the box. Supporting a lower-level protocol would require writing adding maps and tracking code to proxy objects.
|
||||
_libmultiprocess_ uses the [Cap'n Proto](https://capnproto.org) interface description language and protocol, but it could be extended or changed to use a different IDL/protocol like [gRPC](https://grpc.io). The nice thing about _Cap'n Proto_ compared to _gRPC_ and most other lower level protocols is that it allows interface pointers (_Services_ in gRPC parlance) to be passed as method arguments and return values, so object references and bidirectional requests work out of the box. Supporting a lower-level protocol would require adding maps and tracking code to proxy objects.
|
||||
|
||||
_libmultiprocess_ is currently compatible with sandboxing but could add platform-specific sandboxing support or integration with a sandboxing library like [SAPI](https://github.com/google/sandboxed-api).
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
_libmultiprocess_ is a library and code generator that allows calling C++ class interfaces across different processes. For an interface to be available from other processes, it needs two definitions:
|
||||
|
||||
- An **API definition** declaring how the interface is called. Included examples: [calculator.h](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/calculator.h), [printer.h](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/printer.h), [init.h](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/init.h). Bitcoin examples: [node.h](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/interfaces/node.h), [wallet.h](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/interfaces/wallet.h), [echo.h](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/interfaces/echo.h), [init.h](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/interfaces/init.h).
|
||||
- An **API definition** declaring how the interface is called. Included examples: [calculator.h](../example/calculator.h), [printer.h](../example/printer.h), [init.h](../example/init.h). Bitcoin examples: [node.h](https://github.com/bitcoin/bitcoin/blob/master/src/interfaces/node.h), [wallet.h](https://github.com/bitcoin/bitcoin/blob/master/src/interfaces/wallet.h), [echo.h](https://github.com/bitcoin/bitcoin/blob/master/src/interfaces/echo.h), [init.h](https://github.com/bitcoin/bitcoin/blob/master/src/interfaces/init.h).
|
||||
|
||||
- A **data definition** declaring how interface calls get sent across the wire. Included examples: [calculator.capnp](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/calculator.capnp), [printer.capnp](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/printer.capnp), [init.capnp](https://github.com/bitcoin-core/libmultiprocess/blob/master/example/init.capnp). Bitcoin examples: [node.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/node.capnp), [wallet.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/wallet.capnp), [echo.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/echo.capnp), [init.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/init.capnp).
|
||||
- A **data definition** declaring how interface calls get sent across the wire. Included examples: [calculator.capnp](../example/calculator.capnp), [printer.capnp](../example/printer.capnp), [init.capnp](../example/init.capnp). Bitcoin examples: [node.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/node.capnp), [wallet.capnp](https://github.com/ryanofsky/bitcoin/blob/ipc-export/src/ipc/capnp/wallet.capnp), [echo.capnp](https://github.com/bitcoin/bitcoin/blob/master/src/ipc/capnp/echo.capnp), [init.capnp](https://github.com/bitcoin/bitcoin/blob/master/src/ipc/capnp/init.capnp).
|
||||
|
||||
The `*.capnp` data definition files are consumed by the _libmultiprocess_ code generator and each `X.capnp` file generates `X.capnp.c++`, `X.capnp.h`, `X.capnp.proxy-client.c++`, `X.capnp.proxy-server.c++`, `X.capnp.proxy-types.c++`, `X.capnp.proxy-types.h`, and `X.capnp.proxy.h` output files. The generated files include `mp::ProxyClient<Interface>` and `mp::ProxyServer<Interface>` class specializations for all the interfaces in the `.capnp` files. These allow methods on C++ objects in one process to be called from other processes over IPC sockets.
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <kj/async.h>
|
||||
#include <kj/common.h>
|
||||
@@ -37,6 +38,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Exercises deprecated log callback signature
|
||||
static void LogPrint(bool raise, const std::string& message)
|
||||
{
|
||||
if (raise) throw std::runtime_error(message);
|
||||
|
||||
@@ -35,10 +35,10 @@ static auto Spawn(mp::EventLoop& loop, const std::string& process_argv0, const s
|
||||
return std::make_tuple(mp::ConnectStream<InitInterface>(loop, fd), pid);
|
||||
}
|
||||
|
||||
static void LogPrint(bool raise, const std::string& message)
|
||||
static void LogPrint(mp::LogMessage log_data)
|
||||
{
|
||||
if (raise) throw std::runtime_error(message);
|
||||
std::ofstream("debug.log", std::ios_base::app) << message << std::endl;
|
||||
if (log_data.level == mp::Log::Raise) throw std::runtime_error(log_data.message);
|
||||
std::ofstream("debug.log", std::ios_base::app) << log_data.message << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
||||
@@ -32,10 +32,10 @@ public:
|
||||
std::unique_ptr<Printer> makePrinter() override { return std::make_unique<PrinterImpl>(); }
|
||||
};
|
||||
|
||||
static void LogPrint(bool raise, const std::string& message)
|
||||
static void LogPrint(mp::LogMessage log_data)
|
||||
{
|
||||
if (raise) throw std::runtime_error(message);
|
||||
std::ofstream("debug.log", std::ios_base::app) << message << std::endl;
|
||||
if (log_data.level == mp::Log::Raise) throw std::runtime_error(log_data.message);
|
||||
std::ofstream("debug.log", std::ios_base::app) << log_data.message << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
||||
@@ -66,8 +66,6 @@ struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>
|
||||
ProxyClient(const ProxyClient&) = delete;
|
||||
~ProxyClient();
|
||||
|
||||
void setDisconnectCallback(const std::function<void()>& fn);
|
||||
|
||||
//! Reference to callback function that is run if there is a sudden
|
||||
//! disconnect and the Connection object is destroyed before this
|
||||
//! ProxyClient<Thread> object. The callback will destroy this object and
|
||||
@@ -100,36 +98,29 @@ public:
|
||||
EventLoop& m_loop;
|
||||
};
|
||||
|
||||
using LogFn = std::function<void(bool raise, std::string message)>;
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger(bool raise, LogFn& fn) : m_raise(raise), m_fn(fn) {}
|
||||
Logger(Logger&& logger) : m_raise(logger.m_raise), m_fn(logger.m_fn), m_buffer(std::move(logger.m_buffer)) {}
|
||||
~Logger() noexcept(false)
|
||||
{
|
||||
if (m_fn) m_fn(m_raise, m_buffer.str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend Logger& operator<<(Logger& logger, T&& value)
|
||||
{
|
||||
if (logger.m_fn) logger.m_buffer << std::forward<T>(value);
|
||||
return logger;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend Logger& operator<<(Logger&& logger, T&& value)
|
||||
{
|
||||
return logger << std::forward<T>(value);
|
||||
}
|
||||
|
||||
bool m_raise;
|
||||
LogFn& m_fn;
|
||||
std::ostringstream m_buffer;
|
||||
//! Log flags. Update stringify function if changed!
|
||||
enum class Log {
|
||||
Trace = 0,
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Raise,
|
||||
};
|
||||
|
||||
kj::StringPtr KJ_STRINGIFY(Log flags);
|
||||
|
||||
struct LogMessage {
|
||||
|
||||
//! Message to be logged
|
||||
std::string message;
|
||||
|
||||
//! The severity level of this message
|
||||
Log level;
|
||||
};
|
||||
|
||||
using LogFn = std::function<void(LogMessage)>;
|
||||
|
||||
struct LogOptions {
|
||||
|
||||
//! External logging callback.
|
||||
@@ -138,8 +129,60 @@ struct LogOptions {
|
||||
//! Maximum number of characters to use when representing
|
||||
//! request and response structs as strings.
|
||||
size_t max_chars{200};
|
||||
|
||||
//! Messages with a severity level less than log_level will not be
|
||||
//! reported.
|
||||
Log log_level{Log::Trace};
|
||||
};
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger(const LogOptions& options, Log log_level) : m_options(options), m_log_level(log_level) {}
|
||||
|
||||
Logger(Logger&&) = delete;
|
||||
Logger& operator=(Logger&&) = delete;
|
||||
Logger(const Logger&) = delete;
|
||||
Logger& operator=(const Logger&) = delete;
|
||||
|
||||
~Logger() noexcept(false)
|
||||
{
|
||||
if (enabled()) m_options.log_fn({std::move(m_buffer).str(), m_log_level});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend Logger& operator<<(Logger& logger, T&& value)
|
||||
{
|
||||
if (logger.enabled()) logger.m_buffer << std::forward<T>(value);
|
||||
return logger;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend Logger& operator<<(Logger&& logger, T&& value)
|
||||
{
|
||||
return logger << std::forward<T>(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return enabled();
|
||||
}
|
||||
|
||||
private:
|
||||
bool enabled() const
|
||||
{
|
||||
return m_options.log_fn && m_log_level >= m_options.log_level;
|
||||
}
|
||||
|
||||
const LogOptions& m_options;
|
||||
Log m_log_level;
|
||||
std::ostringstream m_buffer;
|
||||
};
|
||||
|
||||
#define MP_LOGPLAIN(loop, ...) if (mp::Logger logger{(loop).m_log_opts, __VA_ARGS__}; logger) logger
|
||||
|
||||
#define MP_LOG(loop, ...) MP_LOGPLAIN(loop, __VA_ARGS__) << "{" << LongThreadName((loop).m_exe_name) << "} "
|
||||
|
||||
std::string LongThreadName(const char* exe_name);
|
||||
|
||||
//! Event loop implementation.
|
||||
@@ -170,8 +213,19 @@ std::string LongThreadName(const char* exe_name);
|
||||
class EventLoop
|
||||
{
|
||||
public:
|
||||
//! Construct event loop object.
|
||||
EventLoop(const char* exe_name, LogFn log_fn, void* context = nullptr);
|
||||
//! Construct event loop object with default logging options.
|
||||
EventLoop(const char* exe_name, LogFn log_fn, void* context = nullptr)
|
||||
: EventLoop(exe_name, LogOptions{std::move(log_fn)}, context){}
|
||||
|
||||
//! Construct event loop object with specified logging options.
|
||||
EventLoop(const char* exe_name, LogOptions log_opts, void* context = nullptr);
|
||||
|
||||
//! Backwards-compatible constructor for previous (deprecated) logging callback signature
|
||||
EventLoop(const char* exe_name, std::function<void(bool, std::string)> old_callback, void* context = nullptr)
|
||||
: EventLoop(exe_name,
|
||||
LogFn{[old_callback = std::move(old_callback)](LogMessage log_data) {old_callback(log_data.level == Log::Raise, std::move(log_data.message));}},
|
||||
context){}
|
||||
|
||||
~EventLoop();
|
||||
|
||||
//! Run event loop. Does not return until shutdown. This should only be
|
||||
@@ -212,15 +266,6 @@ public:
|
||||
//! Check if loop should exit.
|
||||
bool done() const MP_REQUIRES(m_mutex);
|
||||
|
||||
Logger log()
|
||||
{
|
||||
Logger logger(false, m_log_opts.log_fn);
|
||||
logger << "{" << LongThreadName(m_exe_name) << "} ";
|
||||
return logger;
|
||||
}
|
||||
Logger logPlain() { return {false, m_log_opts.log_fn}; }
|
||||
Logger raise() { return {true, m_log_opts.log_fn}; }
|
||||
|
||||
//! Process name included in thread names so combined debug output from
|
||||
//! multiple processes is easier to understand.
|
||||
const char* m_exe_name;
|
||||
@@ -283,18 +328,19 @@ struct Waiter
|
||||
Waiter() = default;
|
||||
|
||||
template <typename Fn>
|
||||
void post(Fn&& fn)
|
||||
bool post(Fn&& fn)
|
||||
{
|
||||
const std::unique_lock<std::mutex> lock(m_mutex);
|
||||
assert(!m_fn);
|
||||
const Lock lock(m_mutex);
|
||||
if (m_fn) return false;
|
||||
m_fn = std::forward<Fn>(fn);
|
||||
m_cv.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Predicate>
|
||||
void wait(std::unique_lock<std::mutex>& lock, Predicate pred)
|
||||
void wait(Lock& lock, Predicate pred)
|
||||
{
|
||||
m_cv.wait(lock, [&] {
|
||||
m_cv.wait(lock.m_lock, [&]() MP_REQUIRES(m_mutex) {
|
||||
// Important for this to be "while (m_fn)", not "if (m_fn)" to avoid
|
||||
// a lost-wakeup bug. A new m_fn and m_cv notification might be sent
|
||||
// after the fn() call and before the lock.lock() call in this loop
|
||||
@@ -317,9 +363,9 @@ struct Waiter
|
||||
//! mutexes than necessary. This mutex can be held at the same time as
|
||||
//! EventLoop::m_mutex as long as Waiter::mutex is locked first and
|
||||
//! EventLoop::m_mutex is locked second.
|
||||
std::mutex m_mutex;
|
||||
Mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::optional<kj::Function<void()>> m_fn;
|
||||
std::optional<kj::Function<void()>> m_fn MP_GUARDED_BY(m_mutex);
|
||||
};
|
||||
|
||||
//! Object holding network & rpc state associated with either an incoming server
|
||||
@@ -544,29 +590,73 @@ void ProxyServerBase<Interface, Impl>::invokeDestroy()
|
||||
CleanupRun(m_context.cleanup_fns);
|
||||
}
|
||||
|
||||
using ConnThreads = std::map<Connection*, ProxyClient<Thread>>;
|
||||
//! Map from Connection to local or remote thread handle which will be used over
|
||||
//! that connection. This map will typically only contain one entry, but can
|
||||
//! contain multiple if a single thread makes IPC calls over multiple
|
||||
//! connections. A std::optional value type is used to avoid the map needing to
|
||||
//! be locked while ProxyClient<Thread> objects are constructed, see
|
||||
//! ThreadContext "Synchronization note" below.
|
||||
using ConnThreads = std::map<Connection*, std::optional<ProxyClient<Thread>>>;
|
||||
using ConnThread = ConnThreads::iterator;
|
||||
|
||||
// Retrieve ProxyClient<Thread> object associated with this connection from a
|
||||
// map, or create a new one and insert it into the map. Return map iterator and
|
||||
// inserted bool.
|
||||
std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex, Connection* connection, const std::function<Thread::Client()>& make_thread);
|
||||
std::tuple<ConnThread, bool> SetThread(GuardedRef<ConnThreads> threads, Connection* connection, const std::function<Thread::Client()>& make_thread);
|
||||
|
||||
//! The thread_local ThreadContext g_thread_context struct provides information
|
||||
//! about individual threads and a way of communicating between them. Because
|
||||
//! it's a thread local struct, each ThreadContext instance is initialized by
|
||||
//! the thread that owns it.
|
||||
//!
|
||||
//! ThreadContext is used for any client threads created externally which make
|
||||
//! IPC calls, and for server threads created by
|
||||
//! ProxyServer<ThreadMap>::makeThread() which execute IPC calls for clients.
|
||||
//!
|
||||
//! In both cases, the struct holds information like the thread name, and a
|
||||
//! Waiter object where the EventLoop can post incoming IPC requests to execute
|
||||
//! on the thread. The struct also holds ConnThread maps associating the thread
|
||||
//! with local and remote ProxyClient<Thread> objects.
|
||||
struct ThreadContext
|
||||
{
|
||||
//! Identifying string for debug.
|
||||
std::string thread_name;
|
||||
|
||||
//! Waiter object used to allow client threads blocked waiting for a server
|
||||
//! response to execute callbacks made from the client's corresponding
|
||||
//! server thread.
|
||||
//! Waiter object used to allow remote clients to execute code on this
|
||||
//! thread. For server threads created by
|
||||
//! ProxyServer<ThreadMap>::makeThread(), this is initialized in that
|
||||
//! function. Otherwise, for client threads created externally, this is
|
||||
//! initialized the first time the thread tries to make an IPC call. Having
|
||||
//! a waiter is necessary for threads making IPC calls in case a server they
|
||||
//! are calling expects them to execute a callback during the call, before
|
||||
//! it sends a response.
|
||||
//!
|
||||
//! For IPC client threads, the Waiter pointer is never cleared and the Waiter
|
||||
//! just gets destroyed when the thread does. For server threads created by
|
||||
//! makeThread(), this pointer is set to null in the ~ProxyServer<Thread> as
|
||||
//! a signal for the thread to exit and destroy itself. In both cases, the
|
||||
//! same Waiter object is used across different calls and only created and
|
||||
//! destroyed once for the lifetime of the thread.
|
||||
std::unique_ptr<Waiter> waiter = nullptr;
|
||||
|
||||
//! When client is making a request to a server, this is the
|
||||
//! `callbackThread` argument it passes in the request, used by the server
|
||||
//! in case it needs to make callbacks into the client that need to execute
|
||||
//! while the client is waiting. This will be set to a local thread object.
|
||||
ConnThreads callback_threads;
|
||||
//!
|
||||
//! Synchronization note: The callback_thread and request_thread maps are
|
||||
//! only ever accessed internally by this thread's destructor and externally
|
||||
//! by Cap'n Proto event loop threads. Since it's possible for IPC client
|
||||
//! threads to make calls over different connections that could have
|
||||
//! different event loops, these maps are guarded by Waiter::m_mutex in case
|
||||
//! different event loop threads add or remove map entries simultaneously.
|
||||
//! However, individual ProxyClient<Thread> objects in the maps will only be
|
||||
//! associated with one event loop and guarded by EventLoop::m_mutex. So
|
||||
//! Waiter::m_mutex does not need to be held while accessing individual
|
||||
//! ProxyClient<Thread> instances, and may even need to be released to
|
||||
//! respect lock order and avoid locking Waiter::m_mutex before
|
||||
//! EventLoop::m_mutex.
|
||||
ConnThreads callback_threads MP_GUARDED_BY(waiter->m_mutex);
|
||||
|
||||
//! When client is making a request to a server, this is the `thread`
|
||||
//! argument it passes in the request, used to control which thread on
|
||||
@@ -575,7 +665,9 @@ struct ThreadContext
|
||||
//! by makeThread. If a client call is being made from a thread currently
|
||||
//! handling a server request, this will be set to the `callbackThread`
|
||||
//! request thread argument passed in that request.
|
||||
ConnThreads request_threads;
|
||||
//!
|
||||
//! Synchronization note: \ref callback_threads note applies here as well.
|
||||
ConnThreads request_threads MP_GUARDED_BY(waiter->m_mutex);
|
||||
|
||||
//! Whether this thread is a capnp event loop thread. Not really used except
|
||||
//! to assert false if there's an attempt to execute a blocking operation
|
||||
@@ -598,7 +690,7 @@ std::unique_ptr<ProxyClient<InitInterface>> ConnectStream(EventLoop& loop, int f
|
||||
init_client = connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<InitInterface>();
|
||||
Connection* connection_ptr = connection.get();
|
||||
connection->onDisconnect([&loop, connection_ptr] {
|
||||
loop.log() << "IPC client: unexpected network disconnect.";
|
||||
MP_LOG(loop, Log::Warning) << "IPC client: unexpected network disconnect.";
|
||||
delete connection_ptr;
|
||||
});
|
||||
});
|
||||
@@ -621,7 +713,7 @@ void _Serve(EventLoop& loop, kj::Own<kj::AsyncIoStream>&& stream, InitImpl& init
|
||||
});
|
||||
auto it = loop.m_incoming_connections.begin();
|
||||
it->onDisconnect([&loop, it] {
|
||||
loop.log() << "IPC server: socket disconnected.";
|
||||
MP_LOG(loop, Log::Info) << "IPC server: socket disconnected.";
|
||||
loop.m_incoming_connections.erase(it);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@ template <typename Client>
|
||||
void clientDestroy(Client& client)
|
||||
{
|
||||
if (client.m_context.connection) {
|
||||
client.m_context.loop->log() << "IPC client destroy " << typeid(client).name();
|
||||
MP_LOG(*client.m_context.loop, Log::Info) << "IPC client destroy " << typeid(client).name();
|
||||
} else {
|
||||
KJ_LOG(INFO, "IPC interrupted client destroy", typeid(client).name());
|
||||
}
|
||||
@@ -577,7 +577,7 @@ void clientDestroy(Client& client)
|
||||
template <typename Server>
|
||||
void serverDestroy(Server& server)
|
||||
{
|
||||
server.m_context.loop->log() << "IPC server destroy " << typeid(server).name();
|
||||
MP_LOG(*server.m_context.loop, Log::Info) << "IPC server destroy " << typeid(server).name();
|
||||
}
|
||||
|
||||
//! Entry point called by generated client code that looks like:
|
||||
@@ -605,7 +605,7 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||
// declaration so the server method runs in a dedicated thread.
|
||||
assert(!g_thread_context.loop_thread);
|
||||
g_thread_context.waiter = std::make_unique<Waiter>();
|
||||
proxy_client.m_context.loop->logPlain()
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
|
||||
<< "{" << g_thread_context.thread_name
|
||||
<< "} IPC client first request from current thread, constructing waiter";
|
||||
}
|
||||
@@ -617,7 +617,7 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||
const char* disconnected = nullptr;
|
||||
proxy_client.m_context.loop->sync([&]() {
|
||||
if (!proxy_client.m_context.connection) {
|
||||
const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
||||
const Lock lock(thread_context.waiter->m_mutex);
|
||||
done = true;
|
||||
disconnected = "IPC client method called after disconnect.";
|
||||
thread_context.waiter->m_cv.notify_all();
|
||||
@@ -629,22 +629,26 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||
using FieldList = typename ProxyClientMethodTraits<typename Request::Params>::Fields;
|
||||
invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
|
||||
IterateFields().handleChain(*invoke_context, request, FieldList(), typename FieldObjs::BuildParams{&fields}...);
|
||||
proxy_client.m_context.loop->logPlain()
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
|
||||
<< "{" << thread_context.thread_name << "} IPC client send "
|
||||
<< TypeName<typename Request::Params>() << " " << LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
|
||||
<< TypeName<typename Request::Params>();
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
|
||||
<< "send data: " << LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
|
||||
|
||||
proxy_client.m_context.loop->m_task_set->add(request.send().then(
|
||||
[&](::capnp::Response<typename Request::Results>&& response) {
|
||||
proxy_client.m_context.loop->logPlain()
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
|
||||
<< "{" << thread_context.thread_name << "} IPC client recv "
|
||||
<< TypeName<typename Request::Results>() << " " << LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
|
||||
<< TypeName<typename Request::Results>();
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
|
||||
<< "recv data: " << LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
|
||||
try {
|
||||
IterateFields().handleChain(
|
||||
*invoke_context, response, FieldList(), typename FieldObjs::ReadResults{&fields}...);
|
||||
} catch (...) {
|
||||
exception = std::current_exception();
|
||||
}
|
||||
const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
||||
const Lock lock(thread_context.waiter->m_mutex);
|
||||
done = true;
|
||||
thread_context.waiter->m_cv.notify_all();
|
||||
},
|
||||
@@ -653,20 +657,20 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||
disconnected = "IPC client method call interrupted by disconnect.";
|
||||
} else {
|
||||
kj_exception = kj::str("kj::Exception: ", e).cStr();
|
||||
proxy_client.m_context.loop->logPlain()
|
||||
MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
|
||||
<< "{" << thread_context.thread_name << "} IPC client exception " << kj_exception;
|
||||
}
|
||||
const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
||||
const Lock lock(thread_context.waiter->m_mutex);
|
||||
done = true;
|
||||
thread_context.waiter->m_cv.notify_all();
|
||||
}));
|
||||
});
|
||||
|
||||
std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
||||
Lock lock(thread_context.waiter->m_mutex);
|
||||
thread_context.waiter->wait(lock, [&done]() { return done; });
|
||||
if (exception) std::rethrow_exception(exception);
|
||||
if (!kj_exception.empty()) proxy_client.m_context.loop->raise() << kj_exception;
|
||||
if (disconnected) proxy_client.m_context.loop->raise() << disconnected;
|
||||
if (!kj_exception.empty()) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << kj_exception;
|
||||
if (disconnected) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << disconnected;
|
||||
}
|
||||
|
||||
//! Invoke callable `fn()` that may return void. If it does return void, replace
|
||||
@@ -700,8 +704,10 @@ kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
|
||||
using Results = typename decltype(call_context.getResults())::Builds;
|
||||
|
||||
int req = ++server_reqs;
|
||||
server.m_context.loop->log() << "IPC server recv request #" << req << " "
|
||||
<< TypeName<typename Params::Reads>() << " " << LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
|
||||
MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server recv request #" << req << " "
|
||||
<< TypeName<typename Params::Reads>();
|
||||
MP_LOG(*server.m_context.loop, Log::Trace) << "request data: "
|
||||
<< LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
|
||||
|
||||
try {
|
||||
using ServerContext = ServerInvokeContext<Server, CallContext>;
|
||||
@@ -717,14 +723,15 @@ kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
|
||||
return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
|
||||
[&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
|
||||
.then([&server, req](CallContext call_context) {
|
||||
server.m_context.loop->log() << "IPC server send response #" << req << " " << TypeName<Results>()
|
||||
<< " " << LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
|
||||
MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server send response #" << req << " " << TypeName<Results>();
|
||||
MP_LOG(*server.m_context.loop, Log::Trace) << "response data: "
|
||||
<< LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
|
||||
});
|
||||
} catch (const std::exception& e) {
|
||||
server.m_context.loop->log() << "IPC server unhandled exception: " << e.what();
|
||||
MP_LOG(*server.m_context.loop, Log::Error) << "IPC server unhandled exception: " << e.what();
|
||||
throw;
|
||||
} catch (...) {
|
||||
server.m_context.loop->log() << "IPC server unhandled exception";
|
||||
MP_LOG(*server.m_context.loop, Log::Error) << "IPC server unhandled exception";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ void CustomBuildField(TypeList<>,
|
||||
// Also store the Thread::Client reference in the callback_threads map so
|
||||
// future calls over this connection can reuse it.
|
||||
auto [callback_thread, _]{SetThread(
|
||||
thread_context.callback_threads, thread_context.waiter->m_mutex, &connection,
|
||||
GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
|
||||
[&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(thread_context, std::thread{})); })};
|
||||
|
||||
// Call remote ThreadMap.makeThread function so server will create a
|
||||
@@ -43,12 +43,12 @@ void CustomBuildField(TypeList<>,
|
||||
return request.send().getResult(); // Nonblocking due to capnp request pipelining.
|
||||
}};
|
||||
auto [request_thread, _1]{SetThread(
|
||||
thread_context.request_threads, thread_context.waiter->m_mutex,
|
||||
GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
|
||||
&connection, make_request_thread)};
|
||||
|
||||
auto context = output.init();
|
||||
context.setThread(request_thread->second.m_client);
|
||||
context.setCallbackThread(callback_thread->second.m_client);
|
||||
context.setThread(request_thread->second->m_client);
|
||||
context.setCallbackThread(callback_thread->second->m_client);
|
||||
}
|
||||
|
||||
//! PassField override for mp.Context arguments. Return asynchronously and call
|
||||
@@ -89,29 +89,39 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
|
||||
// need to update the map.
|
||||
auto& thread_context = g_thread_context;
|
||||
auto& request_threads = thread_context.request_threads;
|
||||
auto [request_thread, inserted]{SetThread(
|
||||
request_threads, thread_context.waiter->m_mutex,
|
||||
server.m_context.connection,
|
||||
[&] { return context_arg.getCallbackThread(); })};
|
||||
ConnThread request_thread;
|
||||
bool inserted;
|
||||
server.m_context.loop->sync([&] {
|
||||
std::tie(request_thread, inserted) = SetThread(
|
||||
GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
|
||||
[&] { return context_arg.getCallbackThread(); });
|
||||
});
|
||||
|
||||
// If an entry was inserted into the requests_threads map,
|
||||
// If an entry was inserted into the request_threads map,
|
||||
// remove it after calling fn.invoke. If an entry was not
|
||||
// inserted, one already existed, meaning this must be a
|
||||
// recursive call (IPC call calling back to the caller which
|
||||
// makes another IPC call), so avoid modifying the map.
|
||||
const bool erase_thread{inserted};
|
||||
KJ_DEFER(if (erase_thread) {
|
||||
std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
||||
// Call erase here with a Connection* argument instead
|
||||
// of an iterator argument, because the `request_thread`
|
||||
// iterator may be invalid if the connection is closed
|
||||
// during this function call. More specifically, the
|
||||
// iterator may be invalid because SetThread adds a
|
||||
// cleanup callback to the Connection destructor that
|
||||
// erases the thread from the map, and also because the
|
||||
// ProxyServer<Thread> destructor calls
|
||||
// request_threads.clear().
|
||||
request_threads.erase(server.m_context.connection);
|
||||
// Erase the request_threads entry on the event loop
|
||||
// thread with loop->sync(), so if the connection is
|
||||
// broken there is not a race between this thread and
|
||||
// the disconnect handler trying to destroy the thread
|
||||
// client object.
|
||||
server.m_context.loop->sync([&] {
|
||||
// Look up the thread again without using existing
|
||||
// iterator since entry may no longer be there after
|
||||
// a disconnect. Destroy node after releasing
|
||||
// Waiter::m_mutex, so the ProxyClient<Thread>
|
||||
// destructor is able to use EventLoop::mutex
|
||||
// without violating lock order.
|
||||
ConnThreads::node_type removed;
|
||||
{
|
||||
Lock lock(thread_context.waiter->m_mutex);
|
||||
removed = request_threads.extract(server.m_context.connection);
|
||||
}
|
||||
});
|
||||
});
|
||||
fn.invoke(server_context, args...);
|
||||
}
|
||||
@@ -140,11 +150,16 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
|
||||
// thread.
|
||||
KJ_IF_MAYBE (thread_server, perhaps) {
|
||||
const auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
|
||||
server.m_context.loop->log()
|
||||
MP_LOG(*server.m_context.loop, Log::Debug)
|
||||
<< "IPC server post request #" << req << " {" << thread.m_thread_context.thread_name << "}";
|
||||
thread.m_thread_context.waiter->post(std::move(invoke));
|
||||
if (!thread.m_thread_context.waiter->post(std::move(invoke))) {
|
||||
MP_LOG(*server.m_context.loop, Log::Error)
|
||||
<< "IPC server error request #" << req
|
||||
<< " {" << thread.m_thread_context.thread_name << "}" << ", thread busy";
|
||||
throw std::runtime_error("thread busy");
|
||||
}
|
||||
} else {
|
||||
server.m_context.loop->log()
|
||||
MP_LOG(*server.m_context.loop, Log::Error)
|
||||
<< "IPC server error request #" << req << ", missing thread to execute request";
|
||||
throw std::runtime_error("invalid thread handle");
|
||||
}
|
||||
|
||||
@@ -182,6 +182,17 @@ public:
|
||||
std::unique_lock<std::mutex> m_lock;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct GuardedRef
|
||||
{
|
||||
Mutex& mutex;
|
||||
T& ref MP_GUARDED_BY(mutex);
|
||||
};
|
||||
|
||||
// CTAD for Clang 16: GuardedRef{mutex, x} -> GuardedRef<decltype(x)>
|
||||
template <class U>
|
||||
GuardedRef(Mutex&, U&) -> GuardedRef<U>;
|
||||
|
||||
//! Analog to std::lock_guard that unlocks instead of locks.
|
||||
template <typename Lock>
|
||||
struct UnlockGuard
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
, enableLibcxx ? false # Whether to use libc++ toolchain and libraries instead of libstdc++
|
||||
, minimal ? false # Whether to create minimal shell without extra tools (faster when cross compiling)
|
||||
, capnprotoVersion ? null
|
||||
, capnprotoSanitizers ? null # Optional sanitizers to build cap'n proto with
|
||||
, cmakeVersion ? null
|
||||
, libcxxSanitizers ? null # Optional LLVM_USE_SANITIZER value to use for libc++, see https://llvm.org/docs/CMake.html
|
||||
}:
|
||||
|
||||
let
|
||||
lib = pkgs.lib;
|
||||
llvm = crossPkgs.llvmPackages_20;
|
||||
llvmBase = crossPkgs.llvmPackages_21;
|
||||
llvm = llvmBase // lib.optionalAttrs (libcxxSanitizers != null) {
|
||||
libcxx = llvmBase.libcxx.override {
|
||||
devExtraCmakeFlags = [ "-DLLVM_USE_SANITIZER=${libcxxSanitizers}" ];
|
||||
};
|
||||
};
|
||||
capnprotoHashes = {
|
||||
"0.7.0" = "sha256-Y/7dUOQPDHjniuKNRw3j8dG1NI9f/aRWpf8V0WzV9k8=";
|
||||
"0.7.1" = "sha256-3cBpVmpvCXyqPUXDp12vCFCk32ZXWpkdOliNH37UwWE=";
|
||||
@@ -34,15 +42,36 @@ let
|
||||
} // (lib.optionalAttrs (lib.versionOlder capnprotoVersion "0.10") {
|
||||
env = { }; # Drop -std=c++20 flag forced by nixpkgs
|
||||
}));
|
||||
capnproto = capnprotoBase.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; });
|
||||
capnproto = (capnprotoBase.overrideAttrs (old: lib.optionalAttrs (capnprotoSanitizers != null) {
|
||||
env = (old.env or { }) // {
|
||||
CXXFLAGS =
|
||||
lib.concatStringsSep " " [
|
||||
(old.env.CXXFLAGS or "")
|
||||
"-fsanitize=${capnprotoSanitizers}"
|
||||
"-fno-omit-frame-pointer"
|
||||
"-g"
|
||||
];
|
||||
};
|
||||
})).override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; });
|
||||
clang = if enableLibcxx then llvm.libcxxClang else llvm.clang;
|
||||
clang-tools = llvm.clang-tools.override { inherit enableLibcxx; };
|
||||
cmakeHashes = {
|
||||
"3.12.4" = "sha256-UlVYS/0EPrcXViz/iULUcvHA5GecSUHYS6raqbKOMZQ=";
|
||||
};
|
||||
cmakeBuild = if cmakeVersion == null then pkgs.cmake else (pkgs.cmake.overrideAttrs (old: {
|
||||
version = cmakeVersion;
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://cmake.org/files/v${lib.versions.majorMinor cmakeVersion}/cmake-${cmakeVersion}.tar.gz";
|
||||
hash = lib.attrByPath [cmakeVersion] "" cmakeHashes;
|
||||
};
|
||||
patches = [];
|
||||
})).override { isMinimalBuild = true; };
|
||||
in crossPkgs.mkShell {
|
||||
buildInputs = [
|
||||
capnproto
|
||||
];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake
|
||||
cmakeBuild
|
||||
include-what-you-use
|
||||
ninja
|
||||
] ++ lib.optionals (!minimal) [
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <capnp/capability.h>
|
||||
#include <capnp/common.h> // IWYU pragma: keep
|
||||
#include <capnp/rpc.h>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
@@ -23,9 +24,9 @@
|
||||
#include <kj/debug.h>
|
||||
#include <kj/function.h>
|
||||
#include <kj/memory.h>
|
||||
#include <kj/string.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -42,7 +43,7 @@ thread_local ThreadContext g_thread_context;
|
||||
void LoggingErrorHandler::taskFailed(kj::Exception&& exception)
|
||||
{
|
||||
KJ_LOG(ERROR, "Uncaught exception in daemonized task.", exception);
|
||||
m_loop.log() << "Uncaught exception in daemonized task.";
|
||||
MP_LOG(m_loop, Log::Error) << "Uncaught exception in daemonized task.";
|
||||
}
|
||||
|
||||
EventLoopRef::EventLoopRef(EventLoop& loop, Lock* lock) : m_loop(&loop), m_lock(lock)
|
||||
@@ -81,6 +82,11 @@ ProxyContext::ProxyContext(Connection* connection) : connection(connection), loo
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
// Connection destructor is always called on the event loop thread. If this
|
||||
// is a local disconnect, it will trigger I/O, so this needs to run on the
|
||||
// event loop thread, and if there was a remote disconnect, this is called
|
||||
// by an onDisconnect callback directly from the event loop thread.
|
||||
assert(std::this_thread::get_id() == m_loop->m_thread_id);
|
||||
// Shut down RPC system first, since this will garbage collect any
|
||||
// ProxyServer objects that were not freed before the connection was closed.
|
||||
// Typically all ProxyServer objects associated with this connection will be
|
||||
@@ -156,6 +162,9 @@ CleanupIt Connection::addSyncCleanup(std::function<void()> fn)
|
||||
|
||||
void Connection::removeSyncCleanup(CleanupIt it)
|
||||
{
|
||||
// Require cleanup functions to be removed on the event loop thread to avoid
|
||||
// needing to deal with them being removed in the middle of a disconnect.
|
||||
assert(std::this_thread::get_id() == m_loop->m_thread_id);
|
||||
const Lock lock(m_loop->m_mutex);
|
||||
m_sync_cleanup_fns.erase(it);
|
||||
}
|
||||
@@ -183,13 +192,13 @@ void EventLoop::addAsyncCleanup(std::function<void()> fn)
|
||||
startAsyncThread();
|
||||
}
|
||||
|
||||
EventLoop::EventLoop(const char* exe_name, LogFn log_fn, void* context)
|
||||
EventLoop::EventLoop(const char* exe_name, LogOptions log_opts, void* context)
|
||||
: m_exe_name(exe_name),
|
||||
m_io_context(kj::setupAsyncIo()),
|
||||
m_task_set(new kj::TaskSet(m_error_handler)),
|
||||
m_log_opts(std::move(log_opts)),
|
||||
m_context(context)
|
||||
{
|
||||
m_log_opts.log_fn = log_fn;
|
||||
int fds[2];
|
||||
KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
|
||||
m_wait_fd = fds[0];
|
||||
@@ -243,9 +252,9 @@ void EventLoop::loop()
|
||||
break;
|
||||
}
|
||||
}
|
||||
log() << "EventLoop::loop done, cancelling event listeners.";
|
||||
MP_LOG(*this, Log::Info) << "EventLoop::loop done, cancelling event listeners.";
|
||||
m_task_set.reset();
|
||||
log() << "EventLoop::loop bye.";
|
||||
MP_LOG(*this, Log::Info) << "EventLoop::loop bye.";
|
||||
wait_stream = nullptr;
|
||||
KJ_SYSCALL(::close(post_fd));
|
||||
const Lock lock(m_mutex);
|
||||
@@ -305,29 +314,34 @@ bool EventLoop::done() const
|
||||
return m_num_clients == 0 && m_async_fns->empty();
|
||||
}
|
||||
|
||||
std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex, Connection* connection, const std::function<Thread::Client()>& make_thread)
|
||||
std::tuple<ConnThread, bool> SetThread(GuardedRef<ConnThreads> threads, Connection* connection, const std::function<Thread::Client()>& make_thread)
|
||||
{
|
||||
const std::unique_lock<std::mutex> lock(mutex);
|
||||
auto thread = threads.find(connection);
|
||||
if (thread != threads.end()) return {thread, false};
|
||||
thread = threads.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(connection),
|
||||
std::forward_as_tuple(make_thread(), connection, /* destroy_connection= */ false)).first;
|
||||
thread->second.setDisconnectCallback([&threads, &mutex, thread] {
|
||||
// Note: it is safe to use the `thread` iterator in this cleanup
|
||||
// function, because the iterator would only be invalid if the map entry
|
||||
// was removed, and if the map entry is removed the ProxyClient<Thread>
|
||||
// destructor unregisters the cleanup.
|
||||
assert(std::this_thread::get_id() == connection->m_loop->m_thread_id);
|
||||
ConnThread thread;
|
||||
bool inserted;
|
||||
{
|
||||
const Lock lock(threads.mutex);
|
||||
std::tie(thread, inserted) = threads.ref.try_emplace(connection);
|
||||
}
|
||||
if (inserted) {
|
||||
thread->second.emplace(make_thread(), connection, /* destroy_connection= */ false);
|
||||
thread->second->m_disconnect_cb = connection->addSyncCleanup([threads, thread] {
|
||||
// Note: it is safe to use the `thread` iterator in this cleanup
|
||||
// function, because the iterator would only be invalid if the map entry
|
||||
// was removed, and if the map entry is removed the ProxyClient<Thread>
|
||||
// destructor unregisters the cleanup.
|
||||
|
||||
// Connection is being destroyed before thread client is, so reset
|
||||
// thread client m_disconnect_cb member so thread client destructor does not
|
||||
// try to unregister this callback after connection is destroyed.
|
||||
// Remove connection pointer about to be destroyed from the map
|
||||
const std::unique_lock<std::mutex> lock(mutex);
|
||||
thread->second.m_disconnect_cb.reset();
|
||||
threads.erase(thread);
|
||||
});
|
||||
return {thread, true};
|
||||
// Connection is being destroyed before thread client is, so reset
|
||||
// thread client m_disconnect_cb member so thread client destructor does not
|
||||
// try to unregister this callback after connection is destroyed.
|
||||
thread->second->m_disconnect_cb.reset();
|
||||
|
||||
// Remove connection pointer about to be destroyed from the map
|
||||
const Lock lock(threads.mutex);
|
||||
threads.ref.erase(thread);
|
||||
});
|
||||
}
|
||||
return {thread, inserted};
|
||||
}
|
||||
|
||||
ProxyClient<Thread>::~ProxyClient()
|
||||
@@ -336,17 +350,18 @@ ProxyClient<Thread>::~ProxyClient()
|
||||
// cleanup callback that was registered to handle the connection being
|
||||
// destroyed before the thread being destroyed.
|
||||
if (m_disconnect_cb) {
|
||||
m_context.connection->removeSyncCleanup(*m_disconnect_cb);
|
||||
// Remove disconnect callback on the event loop thread with
|
||||
// loop->sync(), so if the connection is broken there is not a race
|
||||
// between this thread trying to remove the callback and the disconnect
|
||||
// handler attempting to call it.
|
||||
m_context.loop->sync([&]() {
|
||||
if (m_disconnect_cb) {
|
||||
m_context.connection->removeSyncCleanup(*m_disconnect_cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyClient<Thread>::setDisconnectCallback(const std::function<void()>& fn)
|
||||
{
|
||||
assert(fn);
|
||||
assert(!m_disconnect_cb);
|
||||
m_disconnect_cb = m_context.connection->addSyncCleanup(fn);
|
||||
}
|
||||
|
||||
ProxyServer<Thread>::ProxyServer(ThreadContext& thread_context, std::thread&& thread)
|
||||
: m_thread_context(thread_context), m_thread(std::move(thread))
|
||||
{
|
||||
@@ -364,7 +379,7 @@ ProxyServer<Thread>::~ProxyServer()
|
||||
assert(m_thread_context.waiter.get());
|
||||
std::unique_ptr<Waiter> waiter;
|
||||
{
|
||||
const std::unique_lock<std::mutex> lock(m_thread_context.waiter->m_mutex);
|
||||
const Lock lock(m_thread_context.waiter->m_mutex);
|
||||
//! Reset thread context waiter pointer, as shutdown signal for done
|
||||
//! lambda passed as waiter->wait() argument in makeThread code below.
|
||||
waiter = std::move(m_thread_context.waiter);
|
||||
@@ -398,7 +413,7 @@ kj::Promise<void> ProxyServer<ThreadMap>::makeThread(MakeThreadContext context)
|
||||
g_thread_context.thread_name = ThreadName(m_connection.m_loop->m_exe_name) + " (from " + from + ")";
|
||||
g_thread_context.waiter = std::make_unique<Waiter>();
|
||||
thread_context.set_value(&g_thread_context);
|
||||
std::unique_lock<std::mutex> lock(g_thread_context.waiter->m_mutex);
|
||||
Lock lock(g_thread_context.waiter->m_mutex);
|
||||
// Wait for shutdown signal from ProxyServer<Thread> destructor (signal
|
||||
// is just waiter getting set to null.)
|
||||
g_thread_context.waiter->wait(lock, [] { return !g_thread_context.waiter; });
|
||||
@@ -416,4 +431,16 @@ std::string LongThreadName(const char* exe_name)
|
||||
return g_thread_context.thread_name.empty() ? ThreadName(exe_name) : g_thread_context.thread_name;
|
||||
}
|
||||
|
||||
kj::StringPtr KJ_STRINGIFY(Log v)
|
||||
{
|
||||
switch (v) {
|
||||
case Log::Trace: return "Trace";
|
||||
case Log::Debug: return "Debug";
|
||||
case Log::Info: return "Info";
|
||||
case Log::Warning: return "Warning";
|
||||
case Log::Error: return "Error";
|
||||
case Log::Raise: return "Raise";
|
||||
}
|
||||
return "<Log?>";
|
||||
}
|
||||
} // namespace mp
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <kj/common.h>
|
||||
#include <kj/string-tree.h>
|
||||
#include <pthread.h>
|
||||
@@ -29,6 +31,8 @@
|
||||
#include <pthread_np.h>
|
||||
#endif // HAVE_PTHREAD_GETTHREADID_NP
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace mp {
|
||||
namespace {
|
||||
|
||||
@@ -138,6 +142,9 @@ void ExecProcess(const std::vector<std::string>& args)
|
||||
argv.push_back(nullptr);
|
||||
if (execvp(argv[0], argv.data()) != 0) {
|
||||
perror("execvp failed");
|
||||
if (errno == ENOENT && !args.empty()) {
|
||||
std::cerr << "Missing executable: " << fs::weakly_canonical(args.front()) << '\n';
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ $Proxy.includeTypes("mp/test/foo-types.h");
|
||||
|
||||
interface FooInterface $Proxy.wrap("mp::test::FooImplementation") {
|
||||
add @0 (a :Int32, b :Int32) -> (result :Int32);
|
||||
addOut @19 (a :Int32, b :Int32) -> (ret :Int32);
|
||||
addInOut @20 (x :Int32, sum :Int32) -> (sum :Int32);
|
||||
mapSize @1 (map :List(Pair(Text, Text))) -> (result :Int32);
|
||||
pass @2 (arg :FooStruct) -> (result :FooStruct);
|
||||
raise @3 (arg :FooStruct) -> (error :FooStruct $Proxy.exception("mp::test::FooStruct"));
|
||||
|
||||
@@ -62,6 +62,8 @@ class FooImplementation
|
||||
{
|
||||
public:
|
||||
int add(int a, int b) { return a + b; }
|
||||
void addOut(int a, int b, int& out) { out = a + b; }
|
||||
void addInOut(int x, int& sum) { sum += x; }
|
||||
int mapSize(const std::map<std::string, std::string>& map) { return map.size(); }
|
||||
FooStruct pass(FooStruct foo) { return foo; }
|
||||
void raise(FooStruct foo) { throw foo; }
|
||||
|
||||
@@ -5,26 +5,32 @@
|
||||
#include <mp/test/foo.capnp.h>
|
||||
#include <mp/test/foo.capnp.proxy.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <capnp/capability.h>
|
||||
#include <capnp/rpc.h>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <kj/async.h>
|
||||
#include <kj/async-io.h>
|
||||
#include <kj/common.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/exception.h>
|
||||
#include <kj/memory.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/test.h>
|
||||
#include <memory>
|
||||
#include <mp/proxy.h>
|
||||
#include <mp/proxy.capnp.h>
|
||||
#include <mp/proxy-io.h>
|
||||
#include <mp/util.h>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -60,9 +66,10 @@ public:
|
||||
|
||||
TestSetup(bool client_owns_connection = true)
|
||||
: thread{[&] {
|
||||
EventLoop loop("mptest", [](bool raise, const std::string& log) {
|
||||
std::cout << "LOG" << raise << ": " << log << "\n";
|
||||
if (raise) throw std::runtime_error(log);
|
||||
EventLoop loop("mptest", [](mp::LogMessage log) {
|
||||
// Info logs are not printed by default, but will be shown with `mptest --verbose`
|
||||
KJ_LOG(INFO, log.level, log.message);
|
||||
if (log.level == mp::Log::Raise) throw std::runtime_error(log.message);
|
||||
});
|
||||
auto pipe = loop.m_io_context.provider->newTwoWayPipe();
|
||||
|
||||
@@ -113,6 +120,11 @@ KJ_TEST("Call FooInterface methods")
|
||||
ProxyClient<messages::FooInterface>* foo = setup.client.get();
|
||||
|
||||
KJ_EXPECT(foo->add(1, 2) == 3);
|
||||
int ret;
|
||||
foo->addOut(3, 4, ret);
|
||||
KJ_EXPECT(ret == 7);
|
||||
foo->addInOut(3, ret);
|
||||
KJ_EXPECT(ret == 10);
|
||||
|
||||
FooStruct in;
|
||||
in.name = "name";
|
||||
@@ -297,5 +309,71 @@ KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
|
||||
signal.set_value();
|
||||
}
|
||||
|
||||
KJ_TEST("Make simultaneous IPC calls to trigger 'thread busy' error")
|
||||
{
|
||||
TestSetup setup;
|
||||
ProxyClient<messages::FooInterface>* foo = setup.client.get();
|
||||
std::promise<void> signal;
|
||||
|
||||
foo->initThreadMap();
|
||||
// Use callFnAsync() to get the client to set up the request_thread
|
||||
// that will be used for the test.
|
||||
setup.server->m_impl->m_fn = [&] {};
|
||||
foo->callFnAsync();
|
||||
ThreadContext& tc{g_thread_context};
|
||||
Thread::Client *callback_thread, *request_thread;
|
||||
foo->m_context.loop->sync([&] {
|
||||
Lock lock(tc.waiter->m_mutex);
|
||||
callback_thread = &tc.callback_threads.at(foo->m_context.connection)->m_client;
|
||||
request_thread = &tc.request_threads.at(foo->m_context.connection)->m_client;
|
||||
});
|
||||
|
||||
setup.server->m_impl->m_fn = [&] {
|
||||
try
|
||||
{
|
||||
signal.get_future().get();
|
||||
}
|
||||
catch (const std::future_error& e)
|
||||
{
|
||||
KJ_EXPECT(e.code() == std::make_error_code(std::future_errc::future_already_retrieved));
|
||||
}
|
||||
};
|
||||
|
||||
auto client{foo->m_client};
|
||||
bool caught_thread_busy = false;
|
||||
// NOTE: '3' was chosen because it was the lowest number
|
||||
// of simultaneous calls required to reliably catch a "thread busy" error
|
||||
std::atomic<size_t> running{3};
|
||||
foo->m_context.loop->sync([&]
|
||||
{
|
||||
for (size_t i = 0; i < running; i++)
|
||||
{
|
||||
auto request{client.callFnAsyncRequest()};
|
||||
auto context{request.initContext()};
|
||||
context.setCallbackThread(*callback_thread);
|
||||
context.setThread(*request_thread);
|
||||
foo->m_context.loop->m_task_set->add(request.send().then(
|
||||
[&](auto&& results) {
|
||||
running -= 1;
|
||||
tc.waiter->m_cv.notify_all();
|
||||
},
|
||||
[&](kj::Exception&& e) {
|
||||
KJ_EXPECT(std::string_view{e.getDescription().cStr()} ==
|
||||
"remote exception: std::exception: thread busy");
|
||||
caught_thread_busy = true;
|
||||
running -= 1;
|
||||
signal.set_value();
|
||||
tc.waiter->m_cv.notify_all();
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
{
|
||||
Lock lock(tc.waiter->m_mutex);
|
||||
tc.waiter->wait(lock, [&running] { return running == 0; });
|
||||
}
|
||||
KJ_EXPECT(caught_thread_busy);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mp
|
||||
|
||||
@@ -147,7 +147,6 @@ public:
|
||||
// release ASAP to avoid it where possible.
|
||||
vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd
|
||||
vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9
|
||||
vSeeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us."); // Luke Dashjr
|
||||
vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd
|
||||
vSeeds.emplace_back("seed.btc.petertodd.net."); // Peter Todd, only supports x1, x5, x9, and xd
|
||||
vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost
|
||||
|
||||
11
src/net.cpp
11
src/net.cpp
@@ -574,9 +574,9 @@ void CNode::CloseSocketDisconnect()
|
||||
m_i2p_sam_session.reset();
|
||||
}
|
||||
|
||||
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const {
|
||||
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const {
|
||||
for (const auto& subnet : ranges) {
|
||||
if (subnet.m_subnet.Match(addr)) {
|
||||
if (addr.has_value() && subnet.m_subnet.Match(addr.value())) {
|
||||
NetPermissions::AddFlag(flags, subnet.m_flags);
|
||||
}
|
||||
}
|
||||
@@ -1768,7 +1768,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
||||
{
|
||||
int nInbound = 0;
|
||||
|
||||
AddWhitelistPermissionFlags(permission_flags, addr, vWhitelistedRangeIncoming);
|
||||
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
||||
|
||||
// Tor inbound connections do not reveal the peer's actual network address.
|
||||
// Therefore do not apply address-based whitelist permissions to them.
|
||||
AddWhitelistPermissionFlags(permission_flags, inbound_onion ? std::optional<CNetAddr>{} : addr, vWhitelistedRangeIncoming);
|
||||
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
@@ -1823,7 +1827,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
||||
NodeId id = GetNewNodeId();
|
||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||
|
||||
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
||||
// The V2Transport transparently falls back to V1 behavior when an incoming V1 connection is
|
||||
// detected, so use it whenever we signal NODE_P2P_V2.
|
||||
ServiceFlags local_services = GetLocalServices();
|
||||
|
||||
@@ -1377,7 +1377,7 @@ private:
|
||||
|
||||
bool AttemptToEvictConnection();
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
|
||||
void DeleteNode(CNode* pnode);
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s};
|
||||
static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
|
||||
/** Maximum rate of inventory items to send per second.
|
||||
* Limits the impact of low-fee transaction floods. */
|
||||
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
|
||||
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND{14};
|
||||
/** Target number of tx inventory items to send per transmission. */
|
||||
static constexpr unsigned int INVENTORY_BROADCAST_TARGET = INVENTORY_BROADCAST_PER_SECOND * count_seconds(INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
/** Maximum number of inventory items to send per transmission. */
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#include <node/caches.h>
|
||||
|
||||
#include <common/args.h>
|
||||
#include <common/system.h>
|
||||
#include <index/txindex.h>
|
||||
#include <kernel/caches.h>
|
||||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/byte_units.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -23,16 +26,20 @@ static constexpr size_t MAX_FILTER_INDEX_CACHE{1024_MiB};
|
||||
static constexpr size_t MAX_32BIT_DBCACHE{1024_MiB};
|
||||
|
||||
namespace node {
|
||||
size_t CalculateDbCacheBytes(const ArgsManager& args)
|
||||
{
|
||||
if (auto db_cache{args.GetIntArg("-dbcache")}) {
|
||||
if (*db_cache < 0) db_cache = 0;
|
||||
const uint64_t db_cache_bytes{SaturatingLeftShift<uint64_t>(*db_cache, 20)};
|
||||
constexpr auto max_db_cache{sizeof(void*) == 4 ? MAX_32BIT_DBCACHE : std::numeric_limits<size_t>::max()};
|
||||
return std::max<size_t>(MIN_DB_CACHE, std::min<uint64_t>(db_cache_bytes, max_db_cache));
|
||||
}
|
||||
return DEFAULT_DB_CACHE;
|
||||
}
|
||||
|
||||
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
|
||||
{
|
||||
// Convert -dbcache from MiB units to bytes. The total cache is floored by MIN_DB_CACHE and capped by max size_t value.
|
||||
size_t total_cache{DEFAULT_DB_CACHE};
|
||||
if (std::optional<int64_t> db_cache = args.GetIntArg("-dbcache")) {
|
||||
if (*db_cache < 0) db_cache = 0;
|
||||
uint64_t db_cache_bytes = SaturatingLeftShift<uint64_t>(*db_cache, 20);
|
||||
constexpr auto max_db_cache{sizeof(void*) == 4 ? MAX_32BIT_DBCACHE : std::numeric_limits<size_t>::max()};
|
||||
total_cache = std::max<size_t>(MIN_DB_CACHE, std::min<uint64_t>(db_cache_bytes, max_db_cache));
|
||||
}
|
||||
size_t total_cache{CalculateDbCacheBytes(args)};
|
||||
|
||||
IndexCacheSizes index_sizes;
|
||||
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
|
||||
@@ -44,4 +51,15 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
|
||||
}
|
||||
return {index_sizes, kernel::CacheSizes{total_cache}};
|
||||
}
|
||||
|
||||
void LogOversizedDbCache(const ArgsManager& args) noexcept
|
||||
{
|
||||
if (const auto total_ram{GetTotalRAM()}) {
|
||||
const size_t db_cache{CalculateDbCacheBytes(args)};
|
||||
if (ShouldWarnOversizedDbCache(db_cache, *total_ram)) {
|
||||
InitWarning(bilingual_str{tfm::format(_("A %zu MiB dbcache may be too large for a system memory of only %zu MiB."),
|
||||
db_cache >> 20, *total_ram >> 20)});
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace node
|
||||
|
||||
@@ -27,6 +27,13 @@ struct CacheSizes {
|
||||
kernel::CacheSizes kernel;
|
||||
};
|
||||
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
|
||||
constexpr bool ShouldWarnOversizedDbCache(size_t dbcache, size_t total_ram) noexcept
|
||||
{
|
||||
const size_t cap{(total_ram < 2048_MiB) ? DEFAULT_DB_CACHE : (total_ram / 100) * 75};
|
||||
return dbcache > cap;
|
||||
}
|
||||
|
||||
void LogOversizedDbCache(const ArgsManager& args) noexcept;
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_CACHES_H
|
||||
|
||||
@@ -132,7 +132,6 @@ public:
|
||||
}
|
||||
void appShutdown() override
|
||||
{
|
||||
Interrupt(*m_context);
|
||||
Shutdown(*m_context);
|
||||
}
|
||||
void startShutdown() override
|
||||
@@ -141,12 +140,7 @@ public:
|
||||
if (!(Assert(ctx.shutdown_request))()) {
|
||||
LogError("Failed to send shutdown signal\n");
|
||||
}
|
||||
|
||||
// Stop RPC for clean shutdown if any of waitfor* commands is executed.
|
||||
if (args().GetBoolArg("-server", false)) {
|
||||
InterruptRPC();
|
||||
StopRPC();
|
||||
}
|
||||
Interrupt(*m_context);
|
||||
}
|
||||
bool shutdownRequested() override { return ShutdownRequested(*Assert(m_context)); };
|
||||
bool isSettingIgnored(const std::string& name) override
|
||||
@@ -918,15 +912,21 @@ public:
|
||||
|
||||
std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override
|
||||
{
|
||||
auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options);
|
||||
auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options, m_interrupt_wait);
|
||||
if (new_template) return std::make_unique<BlockTemplateImpl>(m_assemble_options, std::move(new_template), m_node);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void interruptWait() override
|
||||
{
|
||||
InterruptWait(notifications(), m_interrupt_wait);
|
||||
}
|
||||
|
||||
const BlockAssembler::Options m_assemble_options;
|
||||
|
||||
const std::unique_ptr<CBlockTemplate> m_block_template;
|
||||
|
||||
bool m_interrupt_wait{false};
|
||||
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
|
||||
KernelNotifications& notifications() { return *Assert(m_node.notifications); }
|
||||
NodeContext& m_node;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <numeric>
|
||||
|
||||
namespace node {
|
||||
|
||||
@@ -397,8 +398,8 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
||||
|
||||
++nConsecutiveFailed;
|
||||
|
||||
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
|
||||
m_options.nBlockMaxWeight - BLOCK_FULL_ENOUGH_WEIGHT_DELTA) {
|
||||
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight +
|
||||
BLOCK_FULL_ENOUGH_WEIGHT_DELTA > m_options.nBlockMaxWeight) {
|
||||
// Give up if we're close to full and haven't succeeded in a while
|
||||
break;
|
||||
}
|
||||
@@ -453,12 +454,20 @@ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t
|
||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||
}
|
||||
|
||||
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait)
|
||||
{
|
||||
LOCK(kernel_notifications.m_tip_block_mutex);
|
||||
interrupt_wait = true;
|
||||
kernel_notifications.m_tip_block_cv.notify_all();
|
||||
}
|
||||
|
||||
std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainman,
|
||||
KernelNotifications& kernel_notifications,
|
||||
CTxMemPool* mempool,
|
||||
const std::unique_ptr<CBlockTemplate>& block_template,
|
||||
const BlockWaitOptions& options,
|
||||
const BlockAssembler::Options& assemble_options)
|
||||
const BlockAssembler::Options& assemble_options,
|
||||
bool& interrupt_wait)
|
||||
{
|
||||
// Delay calculating the current template fees, just in case a new block
|
||||
// comes in before the next tick.
|
||||
@@ -483,8 +492,12 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
|
||||
// method on BlockTemplate and no template could have been
|
||||
// generated before a tip exists.
|
||||
tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock;
|
||||
return tip_changed || chainman.m_interrupt;
|
||||
return tip_changed || chainman.m_interrupt || interrupt_wait;
|
||||
});
|
||||
if (interrupt_wait) {
|
||||
interrupt_wait = false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (chainman.m_interrupt) return nullptr;
|
||||
@@ -522,18 +535,13 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
|
||||
|
||||
// Calculate the original template total fees if we haven't already
|
||||
if (current_fees == -1) {
|
||||
current_fees = 0;
|
||||
for (CAmount fee : block_template->vTxFees) {
|
||||
current_fees += fee;
|
||||
}
|
||||
current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0});
|
||||
}
|
||||
|
||||
CAmount new_fees = 0;
|
||||
for (CAmount fee : new_tmpl->vTxFees) {
|
||||
new_fees += fee;
|
||||
Assume(options.fee_threshold != MAX_MONEY);
|
||||
if (new_fees >= current_fees + options.fee_threshold) return new_tmpl;
|
||||
}
|
||||
// Check if fees increased enough to return the new template
|
||||
const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
|
||||
Assume(options.fee_threshold != MAX_MONEY);
|
||||
if (new_fees >= current_fees + options.fee_threshold) return new_tmpl;
|
||||
}
|
||||
|
||||
now = NodeClock::now();
|
||||
|
||||
@@ -238,6 +238,9 @@ void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& opti
|
||||
/* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */
|
||||
void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce);
|
||||
|
||||
|
||||
/* Interrupt the current wait for the next block template. */
|
||||
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait);
|
||||
/**
|
||||
* Return a new block template when fees rise to a certain threshold or after a
|
||||
* new tip; return nullopt if timeout is reached.
|
||||
@@ -247,7 +250,8 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
|
||||
CTxMemPool* mempool,
|
||||
const std::unique_ptr<CBlockTemplate>& block_template,
|
||||
const BlockWaitOptions& options,
|
||||
const BlockAssembler::Options& assemble_options);
|
||||
const BlockAssembler::Options& assemble_options,
|
||||
bool& interrupt_wait);
|
||||
|
||||
/* Locks cs_main and returns the block hash and block height of the active chain if it exists; otherwise, returns nullopt.*/
|
||||
std::optional<BlockRef> GetTip(ChainstateManager& chainman);
|
||||
|
||||
@@ -573,7 +573,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="messagesWidget">
|
||||
<widget class="PlainCopyTextEdit" name="messagesWidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
@@ -1868,6 +1868,10 @@
|
||||
<slot>clear()</slot>
|
||||
</slots>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PlainCopyTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../bitcoin.qrc"/>
|
||||
|
||||
@@ -120,7 +120,7 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
<name>AskPassphraseDialog</name>
|
||||
<message>
|
||||
<source>Passphrase Dialog</source>
|
||||
<translation type="unfinished">Změna hesla</translation>
|
||||
<translation type="unfinished">Dialog pro zadání hesla</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter passphrase</source>
|
||||
@@ -132,7 +132,7 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat new passphrase</source>
|
||||
<translation type="unfinished">Totéž heslo ještě jednou</translation>
|
||||
<translation type="unfinished">Zopakujte nové heslo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show passphrase</source>
|
||||
@@ -332,7 +332,7 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
<message>
|
||||
<source>Inbound</source>
|
||||
<extracomment>An inbound connection from a peer. An inbound connection is a connection initiated by a peer.</extracomment>
|
||||
<translation type="unfinished">Sem</translation>
|
||||
<translation type="unfinished">Příchozí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outbound</source>
|
||||
@@ -582,7 +582,7 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Help</source>
|
||||
<translation type="unfinished">Nápověd&a</translation>
|
||||
<translation type="unfinished">&Nápověda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tabs toolbar</source>
|
||||
@@ -686,11 +686,11 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Sending addresses</source>
|
||||
<translation type="unfinished">Odesílací adresy</translation>
|
||||
<translation type="unfinished">&Odesílací adresy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Receiving addresses</source>
|
||||
<translation type="unfinished">Přijímací adresy</translation>
|
||||
<translation type="unfinished">&Přijímací adresy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open a bitcoin: URI</source>
|
||||
@@ -784,11 +784,11 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Hide</source>
|
||||
<translation type="unfinished">Skryj</translation>
|
||||
<translation type="unfinished">&Skryj</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>S&how</source>
|
||||
<translation type="unfinished">Zobraz</translation>
|
||||
<translation type="unfinished">&Zobraz</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%n active connection(s) to Bitcoin network.</source>
|
||||
@@ -1011,7 +1011,7 @@ Signing is only possible with addresses of the type 'legacy'.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy after fee</source>
|
||||
<translation type="unfinished">Kopíruj čistou částku</translation>
|
||||
<translation type="unfinished">Zkopírovat částku po odečtení poplatku</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy bytes</source>
|
||||
@@ -1100,11 +1100,11 @@ If this wallet contains any watchonly scripts, a new wallet will be created whic
|
||||
If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.
|
||||
|
||||
The migration process will create a backup of the wallet before migrating. This backup file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of an incorrect migration, the backup can be restored with the "Restore Wallet" functionality.</source>
|
||||
<translation type="unfinished">Migrace peněženky povede k její přeměně na jednu nebo více deskriptorových peněženek. Bude třeba vytvořit novou zálohu peněženky.
|
||||
Pokud peněženka obsahuje jakékoliv skripty, které lze pouze sledovat, bude vytvořena nová peněženka pouze pro tyto skripty.
|
||||
Pokud tato peněženka obsahuje jakékoliv řešitelné skripty, které nejdou sledovat, bude vytvořena nová jiná peněženka, která bude obsahovat tyto skripty.
|
||||
<translation type="unfinished">Migrace peněženky ji převede na jednu nebo více deskriptorových peněženek. Bude třeba vytvořit novou zálohu peněženky.
|
||||
Pokud tato peněženka obsahuje jakékoli skripty určené pouze ke sledování, bude vytvořena nová peněženka, která bude tyto skripty obsahovat.
|
||||
Pokud tato peněženka obsahuje jakékoli řešitelné, ale nesledované skripty, bude vytvořena další nová peněženka, která bude tyto skripty obsahovat.
|
||||
|
||||
Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se zálohou se bude nazývat <wallet name>-<timestamp>.legacy.bak a bude umístěn v adresáři s touto peněženkou. V případě nepovedené migrace je možné provést obnovu prostřednitvím funkce "Obnovit peněženku".</translation>
|
||||
Proces migrace vytvoří zálohu peněženky před samotnou migrací. Tento záložní soubor se bude jmenovat <wallet name>-<timestamp>.legacy.bak a bude umístěn v adresáři této peněženky. V případě chybné migrace lze zálohu obnovit pomocí funkce "Obnovit peněženku".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Migrate Wallet</source>
|
||||
@@ -1499,7 +1499,7 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
|
||||
<translation type="unfinished">Utrácení bitcoinů, které už utratily zatím nezobrazené transakce, nebude bitcoinovou sítí umožněno.</translation>
|
||||
<translation type="unfinished">Pokus o utracení bitcoinů, které jsou ovlivněny dosud nezobrazenými transakcemi, nebude sítí akceptován.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Number of blocks left</source>
|
||||
@@ -1623,7 +1623,7 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
</message>
|
||||
<message>
|
||||
<source>Font in the Overview tab: </source>
|
||||
<translation type="unfinished">Písmo s pevnou šířkou v panelu Přehled:</translation>
|
||||
<translation type="unfinished">Písmo na kartě Přehled:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options set in this dialog are overridden by the command line:</source>
|
||||
@@ -1770,7 +1770,7 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
</message>
|
||||
<message>
|
||||
<source>&Minimize to the tray instead of the taskbar</source>
|
||||
<translation type="unfinished">&Minimalizovávat do ikony v panelu</translation>
|
||||
<translation type="unfinished">&Minimalizovat do oznamovací oblasti místo na hlavní panel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>M&inimize on close</source>
|
||||
@@ -1816,10 +1816,6 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
<source>Use separate SOCKS&5 proxy to reach peers via Tor onion services:</source>
|
||||
<translation type="unfinished">Použít samostatnou SOCKS&5 proxy ke spojení s protějšky přes skryté služby v Toru:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&OK</source>
|
||||
<translation type="unfinished">&Budiž</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Cancel</source>
|
||||
<translation type="unfinished">&Zrušit</translation>
|
||||
@@ -2022,7 +2018,7 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Transaction Data</source>
|
||||
<translation type="unfinished">Zachovaj procesní data</translation>
|
||||
<translation type="unfinished">Uložit transakční data</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Partially Signed Transaction (Binary)</source>
|
||||
@@ -2079,7 +2075,7 @@ Proces migrace vytvoří zálohu peněženky přes samotnou migrací. Soubor se
|
||||
</message>
|
||||
<message>
|
||||
<source>(But this wallet does not have the right keys.)</source>
|
||||
<translation type="unfinished">Ale tenhle vstup nemá správné klíče</translation>
|
||||
<translation type="unfinished">(Ale tato peněženka nemá správné klíče.)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction is fully signed and ready for broadcast.</source>
|
||||
@@ -2180,7 +2176,7 @@ Pokud vidíte tuto chybu, měli byste požádat, aby obchodník poskytl adresu k
|
||||
<message>
|
||||
<source>Inbound</source>
|
||||
<extracomment>An Inbound Connection from a Peer.</extracomment>
|
||||
<translation type="unfinished">Sem</translation>
|
||||
<translation type="unfinished">Příchozí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outbound</source>
|
||||
@@ -2248,7 +2244,7 @@ Pokud vidíte tuto chybu, měli byste požádat, aby obchodník poskytl adresu k
|
||||
</message>
|
||||
<message>
|
||||
<source>To specify a non-default location of the blocks directory use the '%1' option.</source>
|
||||
<translation type="unfinished">Pro specifikaci neklasické lokace pro data použij možnost '%1'</translation>
|
||||
<translation type="unfinished">Pro zadání jiného umístění adresáře s bloky použijte volbu '%1'.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Startup time</source>
|
||||
@@ -2360,7 +2356,7 @@ Pokud vidíte tuto chybu, měli byste požádat, aby obchodník poskytl adresu k
|
||||
</message>
|
||||
<message>
|
||||
<source>The mapped Autonomous System used for diversifying peer selection.</source>
|
||||
<translation type="unfinished">Mapovaný nezávislý - Autonomní Systém používaný pro rozšírení vzájemného výběru protějsků.</translation>
|
||||
<translation type="unfinished">Mapovaný autonomní systém používaný ke zpestření výběru peerů.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Mapped AS</source>
|
||||
@@ -2394,7 +2390,7 @@ Pokud vidíte tuto chybu, měli byste požádat, aby obchodník poskytl adresu k
|
||||
<message>
|
||||
<source>Addresses Rate-Limited</source>
|
||||
<extracomment>Text title for the Addresses Rate-Limited field in the peer details area, which displays the total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.</extracomment>
|
||||
<translation type="unfinished">Adresy s omezením počtu přijatých adres</translation>
|
||||
<translation type="unfinished">Adresy omezené rychlostí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>User Agent</source>
|
||||
@@ -2406,7 +2402,7 @@ Pokud vidíte tuto chybu, měli byste požádat, aby obchodník poskytl adresu k
|
||||
</message>
|
||||
<message>
|
||||
<source>Current block height</source>
|
||||
<translation type="unfinished">Velikost aktuálního bloku</translation>
|
||||
<translation type="unfinished">Aktuální výška bloku</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
|
||||
@@ -2684,7 +2680,7 @@ For more information on using this console, type %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>Ban for</source>
|
||||
<translation type="unfinished">Uval klatbu na</translation>
|
||||
<translation type="unfinished">Zákaz na</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never</source>
|
||||
@@ -2715,7 +2711,7 @@ For more information on using this console, type %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>An optional label to associate with the new receiving address.</source>
|
||||
<translation type="unfinished">Volitelné označení, které se má přiřadit k nové adrese.</translation>
|
||||
<translation type="unfinished">Volitelný štítek, který se má přiřadit k nové přijímající adrese.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use this form to request payments. All fields are <b>optional</b>.</source>
|
||||
@@ -2727,7 +2723,7 @@ For more information on using this console, type %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</source>
|
||||
<translation type="unfinished">Volitelný popis který sa přidá k téjo nové přijímací adrese (pro jednoduchší identifikaci). Tenhle popis bude také přidán do výzvy k platbě.</translation>
|
||||
<translation type="unfinished">Volitelný štítek k přiřazení nové přijímací adrese (použijete jej k identifikaci faktury). Je také připojen k platební žádosti.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
|
||||
@@ -2759,7 +2755,7 @@ For more information on using this console, type %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove the selected entries from the list</source>
|
||||
<translation type="unfinished">Smaž zvolené požadavky ze seznamu</translation>
|
||||
<translation type="unfinished">Odstranit vybrané položky ze seznamu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
@@ -3000,7 +2996,7 @@ For more information on using this console, type %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide transaction fee settings</source>
|
||||
<translation type="unfinished">Schovat nastavení poplatků transakce - transaction fee</translation>
|
||||
<translation type="unfinished">Skrýt nastavení poplatků za transakci</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
|
||||
@@ -3012,7 +3008,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
|
||||
<translation type="unfinished">Když je zde měně transakcí než místa na bloky, mineři stejně tak relay-e mohou nasadit minimální poplatky. Zaplacením pouze minimálního poplatku je v pohodě, ale mějte na paměti že toto může mít za následek nikdy neověřenou transakci pokud zde bude více bitcoinových transakcí než může síť zvládnout.</translation>
|
||||
<translation type="unfinished">Když je menší objem transakcí než je místa v blocích, mohou těžaři i uzly, které transakce předávají, vyžadovat minimální poplatek. Zaplatit pouze tento minimální poplatek je v pořádku, ale mějte na paměti, že to může vést k tomu, že vaše transakce nikdy nebude potvrzena, pokud bude větší poptávka po bitcoinových transakcích, než kolik síť zvládne zpracovat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
|
||||
@@ -3036,7 +3032,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear &All</source>
|
||||
<translation type="unfinished">Všechno &smaž</translation>
|
||||
<translation type="unfinished">Smazat &vše</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Balance:</source>
|
||||
@@ -3064,7 +3060,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy after fee</source>
|
||||
<translation type="unfinished">Kopíruj čistou částku</translation>
|
||||
<translation type="unfinished">Zkopírovat částku po odečtení poplatku</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy bytes</source>
|
||||
@@ -3094,7 +3090,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Cr&eate Unsigned</source>
|
||||
<translation type="unfinished">Vytvořit bez podpisu</translation>
|
||||
<translation type="unfinished">Vytvořit &bez podpisu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
|
||||
@@ -3128,7 +3124,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Transaction Data</source>
|
||||
<translation type="unfinished">Zachovej procesní data</translation>
|
||||
<translation type="unfinished">Uložit údaje o transakci</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Partially Signed Transaction (Binary)</source>
|
||||
@@ -3315,7 +3311,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter a label for this address to add it to the list of used addresses</source>
|
||||
<translation type="unfinished">Zadej označení této adresy; obojí se ti pak uloží do adresáře</translation>
|
||||
<translation type="unfinished">Zadej popisek pro tuto adresu, aby byla přidána do seznamu použitých adres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source>
|
||||
@@ -3369,7 +3365,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy the current signature to the clipboard</source>
|
||||
<translation type="unfinished">Zkopírovat právě vybraný podpis do schránky</translation>
|
||||
<translation type="unfinished">Zkopírovat aktuální podpis do schránky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign the message to prove you own this Bitcoin address</source>
|
||||
@@ -3381,11 +3377,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset all sign message fields</source>
|
||||
<translation type="unfinished">Vymaž všechna pole formuláře pro podepsání zrávy</translation>
|
||||
<translation type="unfinished">Resetuj všechna pole formuláře pro podepsání zprávy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear &All</source>
|
||||
<translation type="unfinished">Všechno &smaž</translation>
|
||||
<translation type="unfinished">Smazat &vše</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Verify Message</source>
|
||||
@@ -3417,7 +3413,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset all verify message fields</source>
|
||||
<translation type="unfinished">Vymaž všechna pole formuláře pro ověření zrávy</translation>
|
||||
<translation type="unfinished">Resetovat všechna pole formuláře pro ověření zprávy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Click "Sign Message" to generate signature</source>
|
||||
@@ -3429,7 +3425,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satos
|
||||
</message>
|
||||
<message>
|
||||
<source>Please check the address and try again.</source>
|
||||
<translation type="unfinished">Zkontroluj ji prosím a zkus to pak znovu.</translation>
|
||||
<translation type="unfinished">Zkontrolujte prosím adresu a zkuste to znovu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The entered address does not refer to a legacy (P2PKH) key. Message signing for SegWit and other non-P2PKH address types is not supported in this version of %1. Please check the address and try again.</source>
|
||||
@@ -3938,7 +3934,7 @@ Přejděte do Soubor > Otevřít peněženku pro načtení peněženky.
|
||||
</message>
|
||||
<message>
|
||||
<source>Increasing transaction fee failed</source>
|
||||
<translation type="unfinished">Nepodařilo se navýšeit poplatek</translation>
|
||||
<translation type="unfinished">Nepodařilo se navýšit poplatek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you want to increase the fee?</source>
|
||||
@@ -3979,7 +3975,7 @@ Přejděte do Soubor > Otevřít peněženku pro načtení peněženky.
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not commit transaction</source>
|
||||
<translation type="unfinished">Nemohl jsem uložit transakci do peněženky</translation>
|
||||
<translation type="unfinished">Nelze potvrdit transakci</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signer error</source>
|
||||
@@ -4038,7 +4034,7 @@ Přejděte do Soubor > Otevřít peněženku pro načtení peněženky.
|
||||
</message>
|
||||
<message>
|
||||
<source>%s request to listen on port %u. This port is considered "bad" and thus it is unlikely that any peer will connect to it. See doc/p2p-bad-ports.md for details and a full list.</source>
|
||||
<translation type="unfinished"> %s Žádost o poslech na portu 2 %u . Tento port je považován za "špatný", a proto je nepravděpodobné, že by se k němu připojil nějaký peer. Viz doc/p2p-bad-ports.md pro podrobnosti a úplný seznam.</translation>
|
||||
<translation type="unfinished"> %s Žádost o poslech na portu %u . Tento port je považován za "špatný", a proto je nepravděpodobné, že by se k němu připojil nějaký peer. Viz doc/p2p-bad-ports.md pro podrobnosti a úplný seznam.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disk space for %s may not accommodate the block files. Approximately %u GB of data will be stored in this directory.</source>
|
||||
@@ -4131,7 +4127,7 @@ Ověřuji peněženku.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
|
||||
<translation type="unfinished">Jedná se o maximální poplatek, který zaplatíte (navíc k běžnému poplatku), aby se upřednostnila útrata z dosud nepoužitých adres oproti těm už jednou použitých.</translation>
|
||||
<translation type="unfinished">Jedná se o maximální poplatek, který zaplatíte (navíc k běžnému poplatku), abyste upřednostnili vyhýbání se částečnému utrácení před běžným výběrem mincí.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
|
||||
@@ -4273,11 +4269,11 @@ Ověřuji peněženku.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated. They will be removed in a future version.</source>
|
||||
<translation type="unfinished">Možnosti '-datacarrier' a '-datacarriersize' jsou nastaveny, ale jsou označené za zastaralé. V budoucí verzi budou odstraněny.</translation>
|
||||
<translation type="unfinished">Možnosti '-datacarrier' nebo '-datacarriersize' jsou nastaveny, ale jsou označeny jako zastaralé. V některé z budoucích verzí budou odstraněny.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outbound connections restricted to CJDNS (-onlynet=cjdns) but -cjdnsreachable is not provided</source>
|
||||
<translation type="unfinished">Odchozí připojení omezená na CJDNS (-onlynet=cjdns), ale -cjdnsreachable nejsou k dispozici</translation>
|
||||
<translation type="unfinished">Odchozí připojení jsou omezena na CJDNS (-onlynet=cjdns), ale -cjdnsreachable není zadán</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is explicitly forbidden: -onion=0</source>
|
||||
@@ -4293,7 +4289,7 @@ Ověřuji peněženku.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)</source>
|
||||
<translation type="unfinished">Prořezávání: poslední synchronizace peněženky proběhla před už prořezanými daty. Je třeba provést -reindex (tedy v případě režimu prořezávání stáhnout celý blockchain znovu)</translation>
|
||||
<translation type="unfinished">Prořezávání: poslední synchronizace peněženky spadá do již prořezaných dat. Je třeba provést -reindex (v případě prořezávaného uzlu se znovu stáhne celý blockchain).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename of '%s' -> '%s' failed. Cannot clean up the background chainstate leveldb directory.</source>
|
||||
@@ -4367,7 +4363,7 @@ Nelze obnovit zálohu peněženky.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>-paytxfee is deprecated and will be fully removed in v31.0.</source>
|
||||
<translation type="unfinished">Možnost -paytxfee je zastaralá a bude v příští verzi v31.0 plně odstraněna</translation>
|
||||
<translation type="unfinished"> Volba -paytxfee je zastaralá a bude ve verzi 31.0 úplně odstraněna.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A fatal internal error occurred, see debug.log for details: </source>
|
||||
@@ -4575,7 +4571,7 @@ Nelze obnovit zálohu peněženky.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Unable to write data to disk for wallet %s</source>
|
||||
<translation type="unfinished">Chyba: Nepodařilo se zapsat data peněženky %sna disk</translation>
|
||||
<translation type="unfinished">Chyba: Nepodařilo se zapsat data na disk pro peněženku %s</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Unable to write record to new wallet</source>
|
||||
@@ -4695,7 +4691,7 @@ Nelze obnovit zálohu peněženky.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid amount for %s=<amount>: '%s' (must be at least %s)</source>
|
||||
<translation type="unfinished">Neplatná částka %s=<amount>:'%s' (musí být alespoň%s)</translation>
|
||||
<translation type="unfinished">Neplatná částka %s=<amount>:'%s' (musí být alespoň %s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid amount for %s=<amount>: '%s'</source>
|
||||
@@ -4999,7 +4995,7 @@ Nelze obnovit zálohu peněženky.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading %s: Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC).</source>
|
||||
<translation type="unfinished">Chyba při načítání %s: Zastaralý typ peněženky. Převeďte ji prosím na deskriptorovou pomocí převodného nástroje (migratewallet RPC)</translation>
|
||||
<translation type="unfinished">Chyba při načítání %s: Zastaralý typ peněženky. Převeďte ji prosím na deskriptorovou peněženku pomocí migračního nástroje (migratewallet RPC).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Dumpfile specifies an unsupported database format (%s). Only sqlite database dumps are supported</source>
|
||||
@@ -5011,7 +5007,7 @@ Nelze obnovit zálohu peněženky.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction requires one destination of non-zero value, a non-zero feerate, or a pre-selected input</source>
|
||||
<translation type="unfinished">Transakce vyžaduje jeden cíl nenulové hodnoty, nenulový poplatek nebo předvybraný vstup</translation>
|
||||
<translation type="unfinished">Transakce vyžaduje jeden cíl s nenulovou hodnotou, nenulovou sazbu poplatku nebo předem vybraný vstup</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unrecognized descriptor found. Loading wallet %s
|
||||
@@ -5031,7 +5027,7 @@ Zkuste prosím spustit nejnovější verzi softwaru.
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Could not add watchonly tx %s to watchonly wallet</source>
|
||||
<translation type="unfinished">Chyba: Nelze přidat pouze-sledovací tx %s do peněženky pro čtení</translation>
|
||||
<translation type="unfinished">Chyba: Nelze přidat transakci pouze pro sledování %s do peněženky pouze pro sledování</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Could not delete watchonly transactions. </source>
|
||||
@@ -5055,11 +5051,11 @@ Zkuste prosím spustit nejnovější verzi softwaru.
|
||||
</message>
|
||||
<message>
|
||||
<source>Not enough file descriptors available. %d available, %d required.</source>
|
||||
<translation type="unfinished">Nedostatek volných file deskriptorů. %dvolných, %dvyžadovaných</translation>
|
||||
<translation type="unfinished">Nedostatek volných souborových deskriptorů. %dvolných, %dvyžadovaných.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unrecognized network in -proxy='%s': '%s'</source>
|
||||
<translation type="unfinished">Nerozeznaná síť v -proxy='%s': '%s'</translation>
|
||||
<translation type="unfinished">Nerozpoznaná síť v -proxy='%s': '%s'</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>User Agent comment (%s) contains unsafe characters.</source>
|
||||
|
||||
@@ -324,7 +324,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter a Bitcoin address (e.g. %1)</source>
|
||||
<translation type="unfinished">Ingresa una dirección de Bitcoin (Ejemplo: %1)</translation>
|
||||
<translation type="unfinished">Ingresa una dirección de Bitcoin (por ejemplo, %1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unroutable</source>
|
||||
@@ -439,7 +439,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>E&xit</source>
|
||||
<translation type="unfinished">S&alir</translation>
|
||||
<translation type="unfinished">&Salir</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Quit application</source>
|
||||
@@ -508,7 +508,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Options…</source>
|
||||
<translation type="unfinished">&Opciones...</translation>
|
||||
<translation type="unfinished">&Opciones…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Encrypt Wallet…</source>
|
||||
@@ -520,15 +520,15 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Backup Wallet…</source>
|
||||
<translation type="unfinished">&Realizar copia de seguridad del monedero...</translation>
|
||||
<translation type="unfinished">&Realizar copia de seguridad del monedero…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Change Passphrase…</source>
|
||||
<translation type="unfinished">&Cambiar contraseña...</translation>
|
||||
<translation type="unfinished">&Cambiar frase de contraseña…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign &message…</source>
|
||||
<translation type="unfinished">Firmar &mensaje...</translation>
|
||||
<translation type="unfinished">Firmar &mensaje…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
|
||||
@@ -536,7 +536,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Verify message…</source>
|
||||
<translation type="unfinished">&Verificar mensaje...</translation>
|
||||
<translation type="unfinished">&Verificar mensaje…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
|
||||
@@ -687,7 +687,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open a wallet</source>
|
||||
<translation type="unfinished">Abre un monedero</translation>
|
||||
<translation type="unfinished">Abrir monedero</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close wallet</source>
|
||||
@@ -975,7 +975,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Unlock unspent</source>
|
||||
<translation type="unfinished">&Desbloquear lo no gastado</translation>
|
||||
<translation type="unfinished">&Desbloquear salidas no gastadas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy quantity</source>
|
||||
@@ -987,7 +987,7 @@ Solo es posible firmar con direcciones de tipo "legacy".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy after fee</source>
|
||||
<translation type="unfinished">Copiar después de la comisión</translation>
|
||||
<translation type="unfinished">Copiar importe después de comisión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy bytes</source>
|
||||
@@ -1129,7 +1129,7 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
<message>
|
||||
<source>Opening Wallet <b>%1</b>…</source>
|
||||
<extracomment>Descriptive text of the open wallet progress window which indicates to the user which wallet is currently being opened.</extracomment>
|
||||
<translation type="unfinished">Abriendo monedero <b>%1</b>...</translation>
|
||||
<translation type="unfinished">Abriendo monedero <b>%1</b>…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1918,11 +1918,11 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Tx</source>
|
||||
<translation type="unfinished">Firmar Tx</translation>
|
||||
<translation type="unfinished">Firmar transacción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Broadcast Tx</source>
|
||||
<translation type="unfinished">Emitir Tx</translation>
|
||||
<translation type="unfinished">Transmitir transacción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy to Clipboard</source>
|
||||
@@ -1930,7 +1930,7 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>Save…</source>
|
||||
<translation type="unfinished">Guardar...</translation>
|
||||
<translation type="unfinished">Guardar…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
@@ -1954,11 +1954,11 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>Signed %1 inputs, but more signatures are still required.</source>
|
||||
<translation type="unfinished">Se han firmado %1 entradas, pero aún se requieren más firmas.</translation>
|
||||
<translation type="unfinished">Se firmaron %1 entradas, pero aún se requieren más firmas.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signed transaction successfully. Transaction is ready to broadcast.</source>
|
||||
<translation type="unfinished">Se ha firmado correctamente. La transacción está lista para difundirse.</translation>
|
||||
<translation type="unfinished">La transacción se firmó correctamente y está lista para transmitirse.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown error processing transaction.</source>
|
||||
@@ -1970,7 +1970,7 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction broadcast failed: %1</source>
|
||||
<translation type="unfinished">Ha habido un error en la difusión de la transacción: %1</translation>
|
||||
<translation type="unfinished">Error en la transmisión de la transacción: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PSBT copied to clipboard.</source>
|
||||
@@ -2019,7 +2019,7 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction is missing some information about inputs.</source>
|
||||
<translation type="unfinished">Falta alguna información sobre las entradas de la transacción.</translation>
|
||||
<translation type="unfinished">Falta información sobre las entradas de la transacción.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction still needs signature(s).</source>
|
||||
@@ -2027,19 +2027,19 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
</message>
|
||||
<message>
|
||||
<source>(But no wallet is loaded.)</source>
|
||||
<translation type="unfinished">(No existe ningún monedero cargado.)</translation>
|
||||
<translation type="unfinished">(Sin embargo, no se cargó ningún monedero).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(But this wallet cannot sign transactions.)</source>
|
||||
<translation type="unfinished">(Este monedero no puede firmar transacciones.)</translation>
|
||||
<translation type="unfinished">(Este monedero no puede firmar transacciones).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(But this wallet does not have the right keys.)</source>
|
||||
<translation type="unfinished">(Este monedero no tiene las claves adecuadas.)</translation>
|
||||
<translation type="unfinished">(Este monedero no tiene las claves adecuadas).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction is fully signed and ready for broadcast.</source>
|
||||
<translation type="unfinished">La transacción se ha firmado correctamente y está lista para difundirse.</translation>
|
||||
<translation type="unfinished">La transacción se firmó correctamente y está lista para transmitirse.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction status is unknown.</source>
|
||||
@@ -2068,13 +2068,13 @@ El proceso de migración creará una copia de seguridad del monedero antes de mi
|
||||
<source>Cannot process payment request because BIP70 is not supported.
|
||||
Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.
|
||||
If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
|
||||
<translation type="unfinished">No se puede procesar la solicitud de pago debido a que no se mantiene BIP70.
|
||||
Debido a los fallos de seguridad generalizados en el BIP70, se recomienda encarecidamente ignorar las instrucciones del comerciante para cambiar de monedero.
|
||||
<translation type="unfinished">No se puede procesar la solicitud de pago porque no no es compatible con BIP70.
|
||||
Debido a los fallos de seguridad generalizados en BIP70, se recomienda encarecidamente ignorar las instrucciones del comerciante para cambiar de monedero.
|
||||
Si recibe este error, debe solicitar al comerciante que le proporcione un URI compatible con BIP21.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
|
||||
<translation type="unfinished">¡No se puede interpretar la URI! Esto puede deberse a una dirección Bitcoin inválida o a parámetros de URI mal formados.</translation>
|
||||
<translation type="unfinished">No se puede analizar el URI. Esto puede deberse a una dirección Bitcoin inválida o a parámetros de URI con formato incorrecto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Payment request file handling</source>
|
||||
@@ -2086,12 +2086,12 @@ Si recibe este error, debe solicitar al comerciante que le proporcione un URI co
|
||||
<message>
|
||||
<source>User Agent</source>
|
||||
<extracomment>Title of Peers Table column which contains the peer's User Agent string.</extracomment>
|
||||
<translation type="unfinished">Agente del usuario</translation>
|
||||
<translation type="unfinished">Agente de usuario</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Peer</source>
|
||||
<extracomment>Title of Peers Table column which contains a unique number used to identify a connection.</extracomment>
|
||||
<translation type="unfinished">Pareja</translation>
|
||||
<translation type="unfinished">Par</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
@@ -2239,7 +2239,7 @@ Si recibe este error, debe solicitar al comerciante que le proporcione un URI co
|
||||
</message>
|
||||
<message>
|
||||
<source>Memory usage</source>
|
||||
<translation type="unfinished">Memoria utilizada</translation>
|
||||
<translation type="unfinished">Uso de memoria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wallet: </source>
|
||||
@@ -2357,7 +2357,7 @@ Si recibe este error, debe solicitar al comerciante que le proporcione un URI co
|
||||
</message>
|
||||
<message>
|
||||
<source>User Agent</source>
|
||||
<translation type="unfinished">Agente del usuario</translation>
|
||||
<translation type="unfinished">Agente de usuario</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Node window</source>
|
||||
@@ -2543,7 +2543,7 @@ Si recibe este error, debe solicitar al comerciante que le proporcione un URI co
|
||||
</message>
|
||||
<message>
|
||||
<source>the peer selected us for high bandwidth relay</source>
|
||||
<translation type="unfinished">El par nos seleccionó para la retransmisión en ancho de banda alto</translation>
|
||||
<translation type="unfinished">el par nos seleccionó para la retransmisión de gran ancho de banda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>no high bandwidth relay selected</source>
|
||||
@@ -2702,11 +2702,11 @@ Para obtener más información sobre cómo usar esta consola, escribe %6.
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear all fields of the form.</source>
|
||||
<translation type="unfinished">Borrar todos los campos del formulario.</translation>
|
||||
<translation type="unfinished">Vaciar todos los campos del formulario.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear</source>
|
||||
<translation type="unfinished">Borrar</translation>
|
||||
<translation type="unfinished">Vaciar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Requested payments history</source>
|
||||
@@ -2748,10 +2748,6 @@ Para obtener más información sobre cómo usar esta consola, escribe %6.
|
||||
<source>Copy &amount</source>
|
||||
<translation type="unfinished">Copiar &importe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Base58 (Legacy)</source>
|
||||
<translation type="unfinished">Base58 (Heredada)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not recommended due to higher fees and less protection against typos.</source>
|
||||
<translation type="unfinished">No se recomienda debido a las altas comisiones y la poca protección contra errores tipográficos.</translation>
|
||||
@@ -2995,7 +2991,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear &All</source>
|
||||
<translation type="unfinished">Borrar &todo</translation>
|
||||
<translation type="unfinished">Vaciar &todo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Balance:</source>
|
||||
@@ -3023,7 +3019,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy after fee</source>
|
||||
<translation type="unfinished">Copiar tras comisión</translation>
|
||||
<translation type="unfinished">Copiar importe después de comisión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy bytes</source>
|
||||
@@ -3128,7 +3124,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
<message>
|
||||
<source>Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
|
||||
<extracomment>Text to inform a user attempting to create a transaction of their current options. At this stage, a user can send their transaction or create a PSBT. This string is displayed when both private keys and PSBT controls are enabled.</extracomment>
|
||||
<translation type="unfinished">Revisa la transacción. Puedes crear y enviar esta transacción de Bitcoin parcialmente firmada (PSBT), que además puedes guardar o copiar y, luego, firmar; por ejemplo, con un monedero %1 sin conexión o un monedero de hardware compatible con PSBT.</translation>
|
||||
<translation type="unfinished">Revisa la transacción. Puedes crear y enviar esta transacción o crear una transacción de Bitcoin parcialmente firmada (PSBT), que además puedes guardar o copiar y, luego, firmar; por ejemplo, con un monedero %1 sin conexión o un monedero de hardware compatible con PSBT.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please, review your transaction.</source>
|
||||
@@ -3155,11 +3151,11 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>The PSBT has been copied to the clipboard. You can also save it.</source>
|
||||
<translation type="unfinished">Se copió la PSBT al portapapeles. También puedes guardarla.</translation>
|
||||
<translation type="unfinished">Se copió la PSBT al portapapeles. También puedes guardarla.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PSBT saved to disk</source>
|
||||
<translation type="unfinished"> PSBT guardada en disco</translation>
|
||||
<translation type="unfinished">PSBT guardada en disco</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm send coins</source>
|
||||
@@ -3534,11 +3530,11 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Total debit</source>
|
||||
<translation type="unfinished">Total débito</translation>
|
||||
<translation type="unfinished">Débito total</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total credit</source>
|
||||
<translation type="unfinished">Total crédito</translation>
|
||||
<translation type="unfinished">Crédito total</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction fee</source>
|
||||
@@ -3656,7 +3652,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Immature (%1 confirmations, will be available after %2)</source>
|
||||
<translation type="unfinished">Inmadura (%1 confirmaciones; estará disponibles después de %2)</translation>
|
||||
<translation type="unfinished">Inmadura (%1 confirmaciones; estará disponible después de %2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generated but not accepted</source>
|
||||
@@ -3759,7 +3755,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Range…</source>
|
||||
<translation type="unfinished">Intervalo...</translation>
|
||||
<translation type="unfinished">Intervalo…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Copy address</source>
|
||||
@@ -3775,7 +3771,7 @@ Nota: Dado que la comisión se calcula por byte, una tasa de "100 satoshis por k
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy transaction &ID</source>
|
||||
<translation type="unfinished">Copiar &identificador de transacción</translation>
|
||||
<translation type="unfinished">Copiar &ID de transacción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &raw transaction</source>
|
||||
@@ -3880,7 +3876,7 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to decode PSBT from clipboard (invalid base64)</source>
|
||||
<translation type="unfinished">No se puede decodificar la PSBT desde el portapapeles (Base64 inválida)</translation>
|
||||
<translation type="unfinished">No se puede decodificar la PSBT desde el portapapeles (Base64 inválida)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Load Transaction Data</source>
|
||||
@@ -3892,11 +3888,11 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>PSBT file must be smaller than 100 MiB</source>
|
||||
<translation type="unfinished">El archivo de la PSBT debe ser inferior a 100 MiB</translation>
|
||||
<translation type="unfinished">El archivo PSBT debe ser menor de 100 MiB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to decode PSBT</source>
|
||||
<translation type="unfinished">No es posible descodificar la PSBT</translation>
|
||||
<translation type="unfinished">No es posible descodificar la PSBT</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -3940,19 +3936,19 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't draft transaction.</source>
|
||||
<translation type="unfinished">No se puede crear un borrador de la transacción.</translation>
|
||||
<translation type="unfinished">No puede crear un borrador de la transacción.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PSBT copied</source>
|
||||
<translation type="unfinished"> PSBT copiada </translation>
|
||||
<translation type="unfinished">PSBT copiada </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fee-bump PSBT copied to clipboard</source>
|
||||
<translation type="unfinished"> PSBT con incremento de comisión copiada en el portapapeles</translation>
|
||||
<translation type="unfinished">PSBT con incremento de comisión copiada en el portapapeles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't sign transaction.</source>
|
||||
<translation type="unfinished">No se puede firmar la transacción.</translation>
|
||||
<translation type="unfinished">No puede firmar la transacción.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not commit transaction</source>
|
||||
@@ -3964,7 +3960,7 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't display address</source>
|
||||
<translation type="unfinished">No se puede mostrar la dirección</translation>
|
||||
<translation type="unfinished">No puede mostrar la dirección</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4083,15 +4079,15 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
|
||||
<translation type="unfinished">Contribuya si encuentra %s de utilidad. Visite %s para más información acerca del programa.</translation>
|
||||
<translation type="unfinished">Contribuye si te parece que %s es útil. Visita %s para obtener más información acerca del software.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
|
||||
<translation type="unfinished">La poda se ha configurado por debajo del mínimo de %d MiB. Por favor utiliza un valor mas alto.</translation>
|
||||
<translation type="unfinished">La poda se configuró por debajo del mínimo de %d MiB. Usa un valor más alto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.</source>
|
||||
<translation type="unfinished">El modo poda no es compatible con -reindex-chainstate. Haz uso de un -reindex completo en su lugar.</translation>
|
||||
<translation type="unfinished">El modo de poda no es compatible con -reindex-chainstate. Usa -reindex completo en su lugar.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename of '%s' -> '%s' failed. You should resolve this by manually moving or deleting the invalid snapshot directory %s, otherwise you will encounter the same error again on the next startup.</source>
|
||||
@@ -4099,11 +4095,11 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source>
|
||||
<translation type="unfinished">SQLiteDatabase: versión del esquema de la monedero sqlite desconocido %d. Sólo version %d se admite</translation>
|
||||
<translation type="unfinished">SQLiteDatabase: versión del esquema de monedero sqlite desconocido (%d). Solo se admite la versión %d.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
|
||||
<translation type="unfinished">La base de datos de bloques contiene un bloque que parece ser del futuro. Esto puede ser porque la fecha y hora de su equipo están mal ajustados. Reconstruya la base de datos de bloques solo si está seguro de que la fecha y hora de su equipo están ajustadas correctamente.</translation>
|
||||
<translation type="unfinished">La base de datos de bloques contiene un bloque que parece ser del futuro. Esto puede deberse a que la fecha y hora del equipo están mal configuradas. Reconstruye la base de datos de bloques solo tienes la certeza de que la fecha y hora del equipo están configuradas correctamente.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The transaction amount is too small to send after the fee has been deducted</source>
|
||||
@@ -4135,7 +4131,7 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported category-specific logging level %1$s=%2$s. Expected %1$s=<category>:<loglevel>. Valid categories: %3$s. Valid loglevels: %4$s.</source>
|
||||
<translation type="unfinished">El nivel de registro específico de la categoría no es compatible: %1$s=%2$s. Se esperaba %1$s=<category>:<loglevel>. Categorías válidas: %3$s. Niveles de registro válidos: %4 $s.</translation>
|
||||
<translation type="unfinished">El nivel de registro específico de la categoría no es compatible: %1$s=%2$s. Se esperaba %1$s=<category>:<loglevel>. Categorías válidas: %3$s. Niveles de registro válidos: %4$s.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported chainstate database format found. Please restart with -reindex-chainstate. This will rebuild the chainstate database.</source>
|
||||
@@ -4167,11 +4163,11 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot obtain a lock on directory %s. %s is probably already running.</source>
|
||||
<translation type="unfinished">No se puede obtener un bloqueo en el directorio %s. %s probablemente ya se está ejecutando</translation>
|
||||
<translation type="unfinished">No se puede obtener un bloqueo en el directorio %s. %s probablemente ya se está ejecutando.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot resolve -%s address: '%s'</source>
|
||||
<translation type="unfinished">No se puede resolver la dirección de -%s: "%s"</translation>
|
||||
<translation type="unfinished">No se puede resolver - dirección%s: "%s"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot set -forcednsseed to true when setting -dnsseed to false.</source>
|
||||
@@ -4233,11 +4229,11 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6</source>
|
||||
<translation type="unfinished">Opciones incompatibles: -dnsseed=1 se especificó explicitamente, pero -onlynet impide conexiones a IPv4/IPv6</translation>
|
||||
<translation type="unfinished">Opciones incompatibles: -dnsseed=1 se especificó explícitamente, pero -onlynet impide conexiones a IPv4/IPv6</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid amount for %s=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
|
||||
<translation type="unfinished">Importe inválido para %s=<amount>: '%s' (debe ser al menos la comisión mínima de retransmisión de %s para evitar transacciones atascadas)</translation>
|
||||
<translation type="unfinished">Importe inválido para %s=<amount>: "%s" (debe ser al menos la comisión mínima de retransmisión de %s para evitar transacciones atascadas)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Maximum transaction weight is less than transaction weight without inputs</source>
|
||||
@@ -4249,15 +4245,15 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Option '-checkpoints' is set but checkpoints were removed. This option has no effect.</source>
|
||||
<translation type="unfinished">La opción '-checkpoints' está activa pero los puntos de guardado han sido eliminados. Esta opción no tiene efecto.</translation>
|
||||
<translation type="unfinished">La opción "-checkpoints" está configurada, pero se eliminaron los puntos de control. Esta opción no tiene efecto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration.</source>
|
||||
<translation type="unfinished">La opción '-maxorphantx' está activada pero ya no tiene ningún efecto (mirar notas de lanzamiento) . Por favor, elimínala de su configuración.</translation>
|
||||
<translation type="unfinished">La opción "-maxorphantx" está configurada, pero ya no tiene ningún efecto (consultar notas de lanzamiento). Elimínala de la configuración.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated. They will be removed in a future version.</source>
|
||||
<translation type="unfinished">Las opciones '-datacarrier' o '-datacarriersize' están activadas, pero están marcadas como obsoletas. Serán eliminadas en una futura versión. </translation>
|
||||
<translation type="unfinished">Las opciones "-datacarrier" o "-datacarriersize" están configuradas, pero se marcaron como obsoletas. Se eliminarán en una futura versión.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outbound connections restricted to CJDNS (-onlynet=cjdns) but -cjdnsreachable is not provided</source>
|
||||
@@ -4277,11 +4273,11 @@ Ir a "Archivo > Abrir monedero" para cargar uno.
|
||||
</message>
|
||||
<message>
|
||||
<source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)</source>
|
||||
<translation type="unfinished">Poda: la última sincronización sobrepasa los datos podados. Tiene que -reindex (descarga toda la cadena de bloques de nuevo en caso de tener un nodo podado).</translation>
|
||||
<translation type="unfinished">Poda: la última sincronización sobrepasa los datos podados. Debes ejecutar "-reindex" (descargar toda la cadena de bloques de nuevo en caso de tener un nodo podado).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rename of '%s' -> '%s' failed. Cannot clean up the background chainstate leveldb directory.</source>
|
||||
<translation type="unfinished">Error al cambiar el nombre de ''%s" a ''%s". No se puede limpiar el directorio leveldb del estado de la cadena de fondo.</translation>
|
||||
<translation type="unfinished">Error al cambiar el nombre de ''%s" a ''%s". No se puede eliminar el directorio leveldb de chainstate en segundo plano.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Specified -blockmaxweight (%d) exceeds consensus maximum block weight (%d)</source>
|
||||
@@ -4351,7 +4347,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>-paytxfee is deprecated and will be fully removed in v31.0.</source>
|
||||
<translation type="unfinished">-paytxfee está obsoleto y será completamente eliminado en v31.0</translation>
|
||||
<translation type="unfinished">-paytxfee está obsoleto y se eliminará por completo en la versión 31.0.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A fatal internal error occurred, see debug.log for details: </source>
|
||||
@@ -4367,15 +4363,15 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't spend unconfirmed version %d pre-selected input with a version 3 tx</source>
|
||||
<translation type="unfinished">No se puede gastar la versión sin confirmar %d entrada preseleccionada con una version 3 tx</translation>
|
||||
<translation type="unfinished">No se puede gastar una entrada preseleccionada de versión %d sin confirmar con una transacción de versión 3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't spend unconfirmed version 3 pre-selected input with a version %d tx</source>
|
||||
<translation type="unfinished">No se puede gastar una entrada preseleccionada de la versión 3 sin confirmar con una version %d tx</translation>
|
||||
<translation type="unfinished">No se puede gastar una entrada preseleccionada de versión 3 sin confirmar con una transacción de version %d</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot add WalletDescriptor to a non-descriptor wallet</source>
|
||||
<translation type="unfinished">No se puede añadir un monedero basado en descriptores a un monedero no basado en descriptores</translation>
|
||||
<translation type="unfinished">No se puede añadir WalletDescriptor a un monedero no basado en descriptores</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot write to directory '%s'; check permissions.</source>
|
||||
@@ -4403,7 +4399,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not generate scriptPubKeys (cache is empty)</source>
|
||||
<translation type="unfinished">No se ha podido generar scriptPubKeys ( la cache esta vacía)</translation>
|
||||
<translation type="unfinished">No se pudo generar scriptPubKeys (la caché está vacía)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not parse asmap file %s</source>
|
||||
@@ -4487,11 +4483,11 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Couldn't create cursor into database</source>
|
||||
<translation type="unfinished">Error: No se pudo crear el cursor en la base de datos</translation>
|
||||
<translation type="unfinished">Error: No pudo crear el cursor en la base de datos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Disk space is low for %s</source>
|
||||
<translation type="unfinished">Error: El espacio en disco es pequeño para %s</translation>
|
||||
<translation type="unfinished">Error: El espacio en disco es bajo para %s</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
|
||||
@@ -4523,11 +4519,11 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Not all address book records were migrated</source>
|
||||
<translation type="unfinished">Error: No todos los registros del libro de direcciones han sido migrados</translation>
|
||||
<translation type="unfinished">Error: No se migraron todos los registros de la libreta de direcciones.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Not all transaction records were migrated</source>
|
||||
<translation type="unfinished">Error: No todos los registros de transacción han sido migrados</translation>
|
||||
<translation type="unfinished">Error: No se migraron todos los registros de transacciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: This wallet already uses SQLite</source>
|
||||
@@ -4583,15 +4579,15 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to acquire rescan reserver during wallet initialization</source>
|
||||
<translation type="unfinished">No se pudo adquirir la reserva de reescaneo durante la inicialización del monedero </translation>
|
||||
<translation type="unfinished">No se pudo adquirir la reserva de reescaneo durante la inicialización del monedero</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to close block undo file.</source>
|
||||
<translation type="unfinished">No se ha podido cerrar el archivo undo</translation>
|
||||
<translation type="unfinished">No se pudo cerrar el archivo undo de bloques.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to close file when writing block.</source>
|
||||
<translation type="unfinished">No se ha podido cerrar el archivo mientras se escribía un bloque</translation>
|
||||
<translation type="unfinished">No se pudo cerrar el archivo al escribir un bloque.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to connect best block (%s).</source>
|
||||
@@ -4643,7 +4639,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ignoring duplicate -wallet %s.</source>
|
||||
<translation type="unfinished">Ignorar duplicación de -wallet %s.</translation>
|
||||
<translation type="unfinished">Ignorando -wallet %s duplicado.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Incorrect or no genesis block found. Wrong datadir for network?</source>
|
||||
@@ -4715,7 +4711,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading block index…</source>
|
||||
<translation type="unfinished">Cargando índice de bloques...</translation>
|
||||
<translation type="unfinished">Cargando índice de bloques…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading wallet…</source>
|
||||
@@ -4851,7 +4847,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The %s path uses exFAT, which is known to have intermittent corruption problems on macOS. Move this directory to a different filesystem to avoid data loss.</source>
|
||||
<translation type="unfinished">El %s ruta de acceso usa exFAT, el cual es conocido por tener problemas de corrupción intermitente en macOS. Mueve este directorio a un sistema de ficheros distinto para prevenir perdida de datos.</translation>
|
||||
<translation type="unfinished">La ruta %s usa exFAT, que se sabe que presenta problemas de corrupción intermitente en macOS. Mueve este directorio a un sistema de archivos distinto para evitar la pérdida de datos.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The source code is available from %s.</source>
|
||||
@@ -4983,19 +4979,19 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported logging category %s=%s.</source>
|
||||
<translation type="unfinished">La categoría de registro no es compatible %s=%s. </translation>
|
||||
<translation type="unfinished">La categoría de registro no es compatible %s=%s.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading %s: Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC).</source>
|
||||
<translation type="unfinished">Error cargando %s: El monedero es un monedero tipo legacy. Por favor migra a un monedero basado en descriptores usando la herramienta de migrado (migratewallet RPC).</translation>
|
||||
<translation type="unfinished">Error al cargar %s: El monedero es de tipo legacy. Realiza la migración a un monedero basado en descriptores mediante la herramienta de migración (RPC migratewallet).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error: Dumpfile specifies an unsupported database format (%s). Only sqlite database dumps are supported</source>
|
||||
<translation type="unfinished">Error: El archivo de volcado especifica un formato de base de datos no compatible (%s). Sólo se admiten volcados de bases de datos SQLite.</translation>
|
||||
<translation type="unfinished">Error: El archivo de volcado especifica un formato de base de datos no compatible (%s). Solo se admiten volcados de bases de datos SQLite.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to calculate bump fees, because unconfirmed UTXOs depend on an enormous cluster of unconfirmed transactions.</source>
|
||||
<translation type="unfinished">No se ha podido calcular la comisión de incremento porque las UTXO sin confirmar dependen de un grupo enorme de transacciones no confirmadas.</translation>
|
||||
<translation type="unfinished">Las comisiones de incremento no se pudieron calcular, ya que los UTXO sin confirmar dependen de un enorme grupo de transacciones sin confirmar.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transaction requires one destination of non-zero value, a non-zero feerate, or a pre-selected input</source>
|
||||
@@ -5007,7 +5003,7 @@ No se puede restaurar la copia de seguridad del monedero.</translation>
|
||||
The wallet might have been created on a newer version.
|
||||
Please try running the latest software version.
|
||||
</source>
|
||||
<translation type="unfinished">Se encontró un descriptor desconocido. Cargando monedero %s.
|
||||
<translation type="unfinished">Se encontró un descriptor desconocido. Cargando monedero %s
|
||||
|
||||
El monedero se podría haber creado con una versión más reciente.
|
||||
Intenta ejecutar la última versión del software.
|
||||
@@ -5035,11 +5031,11 @@ Intenta ejecutar la última versión del software.
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to start indexes, shutting down…</source>
|
||||
<translation type="unfinished">Error al iniciar los índices, cerrando...</translation>
|
||||
<translation type="unfinished">Error al iniciar los índices, cerrando…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid -proxy address or hostname, ends with '=': '%s'</source>
|
||||
<translation type="unfinished">La dirección -proxy o el nombre de host no es válido, acaba con '=':'%s'</translation>
|
||||
<translation type="unfinished">La dirección -proxy o el nombre de host no son válidos; acaba con "=":"%s"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not enough file descriptors available. %d available, %d required.</source>
|
||||
@@ -5047,7 +5043,7 @@ Intenta ejecutar la última versión del software.
|
||||
</message>
|
||||
<message>
|
||||
<source>Unrecognized network in -proxy='%s': '%s'</source>
|
||||
<translation type="unfinished">Red no reconocida en -proxy:'%s':'%s'</translation>
|
||||
<translation type="unfinished">Red no reconocida en -proxy="%s": "%s"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>User Agent comment (%s) contains unsafe characters.</source>
|
||||
@@ -5059,7 +5055,7 @@ Intenta ejecutar la última versión del software.
|
||||
</message>
|
||||
<message>
|
||||
<source>Verifying wallet(s)…</source>
|
||||
<translation type="unfinished">Verificando monedero(s)...</translation>
|
||||
<translation type="unfinished">Verificando monedero(s)…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wallet needed to be rewritten: restart %s to complete</source>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,6 +72,9 @@ namespace {
|
||||
|
||||
// don't add private key handling cmd's to the history
|
||||
const QStringList historyFilter = QStringList()
|
||||
<< "createwallet"
|
||||
<< "createwalletdescriptor"
|
||||
<< "migratewallet"
|
||||
<< "signmessagewithprivkey"
|
||||
<< "signrawtransactionwithkey"
|
||||
<< "walletpassphrase"
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QCompleter>
|
||||
#include <QMimeData>
|
||||
#include <QTextDocumentFragment>
|
||||
#include <QTextEdit>
|
||||
#include <QThread>
|
||||
#include <QWidget>
|
||||
|
||||
@@ -191,4 +194,20 @@ private Q_SLOTS:
|
||||
void updateAlerts(const QString& warnings);
|
||||
};
|
||||
|
||||
/**
|
||||
* A version of QTextEdit that only populates plaintext mime data from a
|
||||
* selection, this avoids some bad behavior in QT's HTML->Markdown conversion.
|
||||
*/
|
||||
class PlainCopyTextEdit : public QTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QTextEdit::QTextEdit;
|
||||
protected:
|
||||
QMimeData* createMimeDataFromSelection() const override {
|
||||
auto md = new QMimeData();
|
||||
md->setText(textCursor().selection().toPlainText());
|
||||
return md;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_QT_RPCCONSOLE_H
|
||||
|
||||
@@ -52,32 +52,78 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
|
||||
|
||||
void TransactionFilterProxy::setDateRange(const std::optional<QDateTime>& from, const std::optional<QDateTime>& to)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
#endif
|
||||
|
||||
dateFrom = from;
|
||||
dateTo = to;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
||||
#else
|
||||
invalidateFilter();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TransactionFilterProxy::setSearchString(const QString &search_string)
|
||||
{
|
||||
if (m_search_string == search_string) return;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
#endif
|
||||
|
||||
m_search_string = search_string;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
||||
#else
|
||||
invalidateFilter();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TransactionFilterProxy::setTypeFilter(quint32 modes)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
#endif
|
||||
|
||||
this->typeFilter = modes;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
||||
#else
|
||||
invalidateFilter();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TransactionFilterProxy::setMinAmount(const CAmount& minimum)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
#endif
|
||||
|
||||
this->minAmount = minimum;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
||||
#else
|
||||
invalidateFilter();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TransactionFilterProxy::setShowInactive(bool _showInactive)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
beginFilterChange();
|
||||
#endif
|
||||
|
||||
this->showInactive = _showInactive;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
|
||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
||||
#else
|
||||
invalidateFilter();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
|
||||
result.pushKV("mediantime", blockindex.GetMedianTimePast());
|
||||
result.pushKV("nonce", blockindex.nNonce);
|
||||
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
|
||||
result.pushKV("target", GetTarget(tip, pow_limit).GetHex());
|
||||
result.pushKV("target", GetTarget(blockindex, pow_limit).GetHex());
|
||||
result.pushKV("difficulty", GetDifficulty(blockindex));
|
||||
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
|
||||
result.pushKV("nTx", blockindex.nTx);
|
||||
|
||||
@@ -936,8 +936,9 @@ static RPCHelpMan submitpackage()
|
||||
,
|
||||
{
|
||||
{"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
|
||||
"The package must solely consist of a child transaction and all of its unconfirmed parents, if any. None of the parents may depend on each other.\n"
|
||||
"The package must be topologically sorted, with the child being the last element in the array.",
|
||||
"The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n"
|
||||
"None of the parents may depend on each other. Parents that are already in mempool do not need to be present in the package.\n"
|
||||
"The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.",
|
||||
{
|
||||
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
|
||||
},
|
||||
|
||||
@@ -1608,7 +1608,7 @@ static RPCHelpMan finalizepsbt()
|
||||
return RPCHelpMan{"finalizepsbt",
|
||||
"Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
|
||||
"network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
|
||||
"created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
|
||||
"created which has the final_scriptSig and final_scriptwitness fields filled for inputs that are complete.\n"
|
||||
"Implements the Finalizer and Extractor roles.\n",
|
||||
{
|
||||
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user