mirror of https://github.com/bitcoin/bitcoin.git
Compare commits
167 Commits
64d1449ff4
...
4fce466d1a
Author | SHA1 | Date |
---|---|---|
|
4fce466d1a | |
|
1db7bde6d2 | |
|
2db540a173 | |
|
79d3f30ebe | |
|
7918a4d17d | |
|
00000cb55a | |
|
d9d040fd16 | |
|
3a2b8d855d | |
|
e3d4d2461e | |
|
fbbec31efc | |
|
403fb9b120 | |
|
8d9088c5db | |
|
0459e9ac29 | |
|
41f81149c8 | |
|
57a2b40476 | |
|
42c106df05 | |
|
6b1ee82417 | |
|
783db175aa | |
|
dee984def6 | |
|
96651a3f86 | |
|
86cd091d74 | |
|
cc89fcc6da | |
|
4a0f6ba7a7 | |
|
fcb0590623 | |
|
b510893d00 | |
|
ec5841888d | |
|
d735e2e9b3 | |
|
de1dc6b47b | |
|
919e6d01e9 | |
|
452ea59281 | |
|
a33bd767a3 | |
|
2578da69f4 | |
|
25dbe4bc86 | |
|
cfb0d74698 | |
|
86eaa4d6cd | |
|
007900ee9b | |
|
8e47ed6906 | |
|
3635d62f5a | |
|
2e09d66fbb | |
|
156927903d | |
|
e1a1b14c93 | |
|
1ed00a0d39 | |
|
c76de2eea1 | |
|
75353a0163 | |
|
87e7f37918 | |
|
2a4450ccbb | |
|
4268abae1a | |
|
acc7f2a433 | |
|
1aaaaa078b | |
|
fadad7a494 | |
|
50194029e7 | |
|
f41f97240c | |
|
cc4a2cc6bd | |
|
7502d4e940 | |
|
14ae71f323 | |
|
99bc552980 | |
|
576dd97cb9 | |
|
8f73d95221 | |
|
0f7d4ee4e8 | |
|
93a70a42d3 | |
|
6de8051263 | |
|
46135d90ea | |
|
771978952a | |
|
25212dfdb4 | |
|
06df14ba75 | |
|
26e71c237d | |
|
bbe8e9063c | |
|
d4f47f9771 | |
|
fc861332b3 | |
|
3a4d1a25cf | |
|
d8fe258cd6 | |
|
dda5228e02 | |
|
ff05bebcc4 | |
|
200150beba | |
|
7e08445449 | |
|
7b5261f7ef | |
|
65e909dfdd | |
|
31b29f8eb6 | |
|
e62e0a12b3 | |
|
7ae0497eef | |
|
05d984b1a4 | |
|
b807dfcdc5 | |
|
d41b503ae1 | |
|
5ae8edbc30 | |
|
df67bb6fd8 | |
|
44a493e150 | |
|
ad4a49090d | |
|
dd61f08fd5 | |
|
350692e561 | |
|
94db966a3b | |
|
eca50854e1 | |
|
89144eb473 | |
|
eaa1a3cd0b | |
|
b77137a564 | |
|
a86e1a6e32 | |
|
6861dadfcb | |
|
3b3ab3a50a | |
|
2738b63e02 | |
|
fbde8d9a81 | |
|
34fefb6335 | |
|
56791b5829 | |
|
337a6e7386 | |
|
451ba9ada4 | |
|
77b2ebb811 | |
|
bf7996cbc3 | |
|
4c3c1f42cf | |
|
953544d028 | |
|
df101c97c2 | |
|
56c6daa64f | |
|
79752b9c0b | |
|
cad9a7fd73 | |
|
6a33970fef | |
|
edb871cba2 | |
|
2427939935 | |
|
d6aa266d43 | |
|
eaf2c46475 | |
|
5aec516b2c | |
|
316a0c5132 | |
|
74fa028da1 | |
|
168360f4ae | |
|
6c720459be | |
|
e9c52272eb | |
|
c49a43591f | |
|
535fa0ad0d | |
|
453b0fa286 | |
|
1444ed855f | |
|
29e836fae6 | |
|
0972f55040 | |
|
2b0cd1f3fb | |
|
ef20c2d11d | |
|
1ff9e92948 | |
|
f563ce9081 | |
|
947bed28fe | |
|
7584a4fda9 | |
|
67f632b6de | |
|
c4adfbf706 | |
|
5aa3d3135d | |
|
bdf01c6f61 | |
|
0a26731c4c | |
|
2d6a0c4649 | |
|
9193c3e434 | |
|
28efd724b4 | |
|
75e6984ec8 | |
|
652424ad16 | |
|
00c253d494 | |
|
ff18b6bbaf | |
|
8e434a8499 | |
|
05353d9cf0 | |
|
fabc2615af | |
|
417437eb01 | |
|
3cbbcb66ef | |
|
bddcadee82 | |
|
a5ead122fe | |
|
4577fb2b1e | |
|
a3986935f0 | |
|
5db8cd2d37 | |
|
adefb51c54 | |
|
88b0647f02 | |
|
8a08eef645 | |
|
0802398e74 | |
|
6d9e5d130d | |
|
3265df63a4 | |
|
91cbf4dbd8 | |
|
50da7432ec | |
|
e6a917c8f8 | |
|
e883b37768 | |
|
b81f37031c |
|
@ -1,9 +1,12 @@
|
||||||
name: 'Configure Docker'
|
name: 'Configure Docker'
|
||||||
description: 'Set up Docker build driver and configure build cache args'
|
description: 'Set up Docker build driver and configure build cache args'
|
||||||
inputs:
|
inputs:
|
||||||
use-cirrus:
|
cache-provider:
|
||||||
description: 'Use cirrus cache'
|
description: 'gha or cirrus cache provider'
|
||||||
required: true
|
required: true
|
||||||
|
options:
|
||||||
|
- gh
|
||||||
|
- cirrus
|
||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
|
@ -32,7 +35,7 @@ runs:
|
||||||
# which are set automatically when running on GitHub infra: https://docs.docker.com/build/cache/backends/gha/#synopsis
|
# which are set automatically when running on GitHub infra: https://docs.docker.com/build/cache/backends/gha/#synopsis
|
||||||
|
|
||||||
# Use cirrus cache host
|
# 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}"
|
url_args="url=${CIRRUS_CACHE_HOST},url_v2=${CIRRUS_CACHE_HOST}"
|
||||||
else
|
else
|
||||||
url_args=""
|
url_args=""
|
||||||
|
|
|
@ -42,6 +42,8 @@ def main():
|
||||||
"-DBUILD_BENCH=ON",
|
"-DBUILD_BENCH=ON",
|
||||||
"-DBUILD_FUZZ_BINARY=ON",
|
"-DBUILD_FUZZ_BINARY=ON",
|
||||||
"-DWITH_USDT=ON",
|
"-DWITH_USDT=ON",
|
||||||
|
"-DBUILD_KERNEL_LIB=ON",
|
||||||
|
"-DBUILD_KERNEL_TEST=ON",
|
||||||
"-DCMAKE_CXX_FLAGS=-Wno-error=unused-member-function",
|
"-DCMAKE_CXX_FLAGS=-Wno-error=unused-member-function",
|
||||||
])
|
])
|
||||||
run(["cmake", "--build", "build", "-j", str(num_procs)])
|
run(["cmake", "--build", "build", "-j", str(num_procs)])
|
||||||
|
|
|
@ -33,15 +33,15 @@ jobs:
|
||||||
name: 'determine runners'
|
name: 'determine runners'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
use-cirrus-runners: ${{ steps.runners.outputs.use-cirrus-runners }}
|
provider: ${{ steps.runners.outputs.provider }}
|
||||||
steps:
|
steps:
|
||||||
- id: runners
|
- id: runners
|
||||||
run: |
|
run: |
|
||||||
if [[ "${REPO_USE_CIRRUS_RUNNERS}" == "${{ github.repository }}" ]]; then
|
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"
|
echo "::notice title=Runner Selection::Using Cirrus Runners"
|
||||||
else
|
else
|
||||||
echo "use-cirrus-runners=false" >> "$GITHUB_OUTPUT"
|
echo "provider=gha" >> "$GITHUB_OUTPUT"
|
||||||
echo "::notice title=Runner Selection::Using GitHub-hosted runners"
|
echo "::notice title=Runner Selection::Using GitHub-hosted runners"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ jobs:
|
||||||
name: ${{ matrix.job-name }}
|
name: ${{ matrix.job-name }}
|
||||||
# Use any image to support the xcode-select below, but hardcode version to avoid silent upgrades (and breaks).
|
# Use any image to support the xcode-select below, but hardcode version to avoid silent upgrades (and breaks).
|
||||||
# See: https://github.com/actions/runner-images#available-images.
|
# See: https://github.com/actions/runner-images#available-images.
|
||||||
runs-on: macos-14
|
runs-on: macos-15
|
||||||
|
|
||||||
# When a contributor maintains a fork of the repo, any pull request they make
|
# When a contributor maintains a fork of the repo, any pull request they make
|
||||||
# to their own fork, or to the main repository, will trigger two CI runs:
|
# to their own fork, or to the main repository, will trigger two CI runs:
|
||||||
|
@ -123,10 +123,10 @@ jobs:
|
||||||
include:
|
include:
|
||||||
- job-type: standard
|
- job-type: standard
|
||||||
file-env: './ci/test/00_setup_env_mac_native.sh'
|
file-env: './ci/test/00_setup_env_mac_native.sh'
|
||||||
job-name: 'macOS 14 native, arm64, no depends, sqlite only, gui'
|
job-name: 'macOS native, no depends, sqlite only, gui'
|
||||||
- job-type: fuzz
|
- job-type: fuzz
|
||||||
file-env: './ci/test/00_setup_env_mac_native_fuzz.sh'
|
file-env: './ci/test/00_setup_env_mac_native_fuzz.sh'
|
||||||
job-name: 'macOS 14 native, arm64, fuzz'
|
job-name: 'macOS native, fuzz'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DANGER_RUN_CI_ON_HOST: 1
|
DANGER_RUN_CI_ON_HOST: 1
|
||||||
|
@ -145,8 +145,8 @@ jobs:
|
||||||
# Use the earliest Xcode supported by the version of macOS denoted in
|
# Use the earliest Xcode supported by the version of macOS denoted in
|
||||||
# doc/release-notes-empty-template.md and providing at least the
|
# doc/release-notes-empty-template.md and providing at least the
|
||||||
# minimum clang version denoted in doc/dependencies.md.
|
# minimum clang version denoted in doc/dependencies.md.
|
||||||
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes
|
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-16-release-notes
|
||||||
sudo xcode-select --switch /Applications/Xcode_15.0.app
|
sudo xcode-select --switch /Applications/Xcode_16.0.app
|
||||||
clang --version
|
clang --version
|
||||||
|
|
||||||
- name: Install Homebrew packages
|
- name: Install Homebrew packages
|
||||||
|
@ -202,7 +202,7 @@ jobs:
|
||||||
job-type: [standard, fuzz]
|
job-type: [standard, fuzz]
|
||||||
include:
|
include:
|
||||||
- job-type: standard
|
- job-type: standard
|
||||||
generate-options: '-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DWERROR=ON'
|
generate-options: '-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DBUILD_KERNEL_LIB=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_TEST=OFF -DWERROR=ON'
|
||||||
job-name: 'Windows native, VS 2022'
|
job-name: 'Windows native, VS 2022'
|
||||||
- job-type: fuzz
|
- job-type: fuzz
|
||||||
generate-options: '-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON -DVCPKG_MANIFEST_FEATURES="wallet" -DBUILD_GUI=OFF -DBUILD_FOR_FUZZING=ON -DWERROR=ON'
|
generate-options: '-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON -DVCPKG_MANIFEST_FEATURES="wallet" -DBUILD_GUI=OFF -DBUILD_FOR_FUZZING=ON -DWERROR=ON'
|
||||||
|
@ -211,11 +211,16 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- *CHECKOUT
|
- *CHECKOUT
|
||||||
|
|
||||||
- name: Configure Developer Command Prompt for Microsoft Visual C++
|
- &SET_UP_VS
|
||||||
# Using microsoft/setup-msbuild is not enough.
|
name: Set up VS Developer Prompt
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||||
with:
|
run: |
|
||||||
arch: x64
|
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
$installationPath = & $vswherePath -latest -property installationPath
|
||||||
|
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | foreach-object {
|
||||||
|
$name, $value = $_ -split '=', 2
|
||||||
|
echo "$name=$value" >> $env:GITHUB_ENV
|
||||||
|
}
|
||||||
|
|
||||||
- name: Get tool information
|
- name: Get tool information
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
@ -263,14 +268,26 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cmake --build . -j $NUMBER_OF_PROCESSORS --config Release
|
cmake --build . -j $NUMBER_OF_PROCESSORS --config Release
|
||||||
|
|
||||||
- name: Get bitcoind manifest
|
- name: Check executable manifests
|
||||||
if: matrix.job-type == 'standard'
|
if: matrix.job-type == 'standard'
|
||||||
working-directory: build
|
working-directory: build
|
||||||
|
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||||
run: |
|
run: |
|
||||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -out:bitcoind.manifest
|
mt.exe -nologo -inputresource:bin\Release\bitcoind.exe -out:bitcoind.manifest
|
||||||
cat bitcoind.manifest
|
Get-Content bitcoind.manifest
|
||||||
echo
|
|
||||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -validate_manifest
|
Get-ChildItem -Filter "bin\Release\*.exe" | ForEach-Object {
|
||||||
|
$exeName = $_.Name
|
||||||
|
|
||||||
|
# Skip as they currently do not have manifests
|
||||||
|
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe" -or $exeName -eq "test_bitcoin-qt.exe") {
|
||||||
|
Write-Host "Skipping $exeName (no manifest present)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Checking $exeName"
|
||||||
|
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||||
|
}
|
||||||
|
|
||||||
- name: Run test suite
|
- name: Run test suite
|
||||||
if: matrix.job-type == 'standard'
|
if: matrix.job-type == 'standard'
|
||||||
|
@ -290,6 +307,7 @@ jobs:
|
||||||
BITCOINTX: '${{ github.workspace }}\build\bin\Release\bitcoin-tx.exe'
|
BITCOINTX: '${{ github.workspace }}\build\bin\Release\bitcoin-tx.exe'
|
||||||
BITCOINUTIL: '${{ github.workspace }}\build\bin\Release\bitcoin-util.exe'
|
BITCOINUTIL: '${{ github.workspace }}\build\bin\Release\bitcoin-util.exe'
|
||||||
BITCOINWALLET: '${{ github.workspace }}\build\bin\Release\bitcoin-wallet.exe'
|
BITCOINWALLET: '${{ github.workspace }}\build\bin\Release\bitcoin-wallet.exe'
|
||||||
|
BITCOINCHAINSTATE: '${{ github.workspace }}\build\bin\Release\bitcoin-chainstate.exe'
|
||||||
TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }}
|
TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }}
|
||||||
run: py -3 test/functional/test_runner.py --jobs $NUMBER_OF_PROCESSORS --ci --quiet --tmpdirprefix="${RUNNER_TEMP}" --combinedlogslen=99999999 --timeout-factor=${TEST_RUNNER_TIMEOUT_FACTOR} ${TEST_RUNNER_EXTRA}
|
run: py -3 test/functional/test_runner.py --jobs $NUMBER_OF_PROCESSORS --ci --quiet --tmpdirprefix="${RUNNER_TEMP}" --combinedlogslen=99999999 --timeout-factor=${TEST_RUNNER_TIMEOUT_FACTOR} ${TEST_RUNNER_EXTRA}
|
||||||
|
|
||||||
|
@ -312,7 +330,7 @@ jobs:
|
||||||
windows-cross:
|
windows-cross:
|
||||||
name: 'Linux->Windows cross, no tests'
|
name: 'Linux->Windows cross, no tests'
|
||||||
needs: runners
|
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' }}
|
if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
@ -332,7 +350,7 @@ jobs:
|
||||||
- name: Configure Docker
|
- name: Configure Docker
|
||||||
uses: ./.github/actions/configure-docker
|
uses: ./.github/actions/configure-docker
|
||||||
with:
|
with:
|
||||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
cache-provider: ${{ needs.runners.outputs.provider }}
|
||||||
|
|
||||||
- name: CI script
|
- name: CI script
|
||||||
run: ./ci/test_run_all.sh
|
run: ./ci/test_run_all.sh
|
||||||
|
@ -370,19 +388,26 @@ jobs:
|
||||||
- name: Run bitcoind.exe
|
- name: Run bitcoind.exe
|
||||||
run: ./bin/bitcoind.exe -version
|
run: ./bin/bitcoind.exe -version
|
||||||
|
|
||||||
- name: Find mt.exe tool
|
- *SET_UP_VS
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$sdk_dir = (Get-ItemProperty 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots' -Name KitsRoot10).KitsRoot10
|
|
||||||
$sdk_latest = (Get-ChildItem "$sdk_dir\bin" -Directory | Where-Object { $_.Name -match '^\d+\.\d+\.\d+\.\d+$' } | Sort-Object Name -Descending | Select-Object -First 1).Name
|
|
||||||
"MT_EXE=${sdk_dir}bin\${sdk_latest}\x64\mt.exe" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Get bitcoind manifest
|
- name: Check executable manifests
|
||||||
shell: pwsh
|
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||||
run: |
|
run: |
|
||||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
mt.exe -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
||||||
Get-Content bitcoind.manifest
|
Get-Content bitcoind.manifest
|
||||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -validate_manifest
|
|
||||||
|
Get-ChildItem -Filter "bin\*.exe" | ForEach-Object {
|
||||||
|
$exeName = $_.Name
|
||||||
|
|
||||||
|
# Skip as they currently do not have manifests
|
||||||
|
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe") {
|
||||||
|
Write-Host "Skipping $exeName (no manifest present)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Checking $exeName"
|
||||||
|
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||||
|
}
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
# Can't use ctest here like other jobs as we don't have a CMake build tree.
|
# Can't use ctest here like other jobs as we don't have a CMake build tree.
|
||||||
|
@ -422,7 +447,7 @@ jobs:
|
||||||
ci-matrix:
|
ci-matrix:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
needs: runners
|
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' }}
|
if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }}
|
||||||
timeout-minutes: ${{ matrix.timeout-minutes }}
|
timeout-minutes: ${{ matrix.timeout-minutes }}
|
||||||
|
|
||||||
|
@ -439,6 +464,7 @@ jobs:
|
||||||
fallback-runner: 'ubuntu-24.04-arm'
|
fallback-runner: 'ubuntu-24.04-arm'
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
file-env: './ci/test/00_setup_env_arm.sh'
|
file-env: './ci/test/00_setup_env_arm.sh'
|
||||||
|
provider: 'gha'
|
||||||
|
|
||||||
- name: 'ASan + LSan + UBSan + integer, no depends, USDT'
|
- 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
|
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 +539,7 @@ jobs:
|
||||||
- name: Configure Docker
|
- name: Configure Docker
|
||||||
uses: ./.github/actions/configure-docker
|
uses: ./.github/actions/configure-docker
|
||||||
with:
|
with:
|
||||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
cache-provider: ${{ matrix.provider || needs.runners.outputs.provider }}
|
||||||
|
|
||||||
- name: Enable bpfcc script
|
- name: Enable bpfcc script
|
||||||
if: ${{ env.CONTAINER_NAME == 'ci_native_asan' }}
|
if: ${{ env.CONTAINER_NAME == 'ci_native_asan' }}
|
||||||
|
@ -550,7 +576,7 @@ jobs:
|
||||||
- name: Configure Docker
|
- name: Configure Docker
|
||||||
uses: ./.github/actions/configure-docker
|
uses: ./.github/actions/configure-docker
|
||||||
with:
|
with:
|
||||||
use-cirrus: ${{ needs.runners.outputs.use-cirrus-runners }}
|
cache-provider: ${{ needs.runners.outputs.provider }}
|
||||||
|
|
||||||
- name: CI script
|
- name: CI script
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -105,6 +105,7 @@ option(BUILD_UTIL "Build bitcoin-util executable." ${BUILD_TESTS})
|
||||||
|
|
||||||
option(BUILD_UTIL_CHAINSTATE "Build experimental bitcoin-chainstate executable." OFF)
|
option(BUILD_UTIL_CHAINSTATE "Build experimental bitcoin-chainstate executable." OFF)
|
||||||
option(BUILD_KERNEL_LIB "Build experimental bitcoinkernel library." ${BUILD_UTIL_CHAINSTATE})
|
option(BUILD_KERNEL_LIB "Build experimental bitcoinkernel library." ${BUILD_UTIL_CHAINSTATE})
|
||||||
|
option(BUILD_KERNEL_TEST "Build tests for the experimental bitcoinkernel library." ${BUILD_KERNEL_LIB})
|
||||||
|
|
||||||
option(ENABLE_WALLET "Enable wallet." ON)
|
option(ENABLE_WALLET "Enable wallet." ON)
|
||||||
if(ENABLE_WALLET)
|
if(ENABLE_WALLET)
|
||||||
|
@ -210,6 +211,7 @@ if(BUILD_FOR_FUZZING)
|
||||||
set(BUILD_UTIL OFF)
|
set(BUILD_UTIL OFF)
|
||||||
set(BUILD_UTIL_CHAINSTATE OFF)
|
set(BUILD_UTIL_CHAINSTATE OFF)
|
||||||
set(BUILD_KERNEL_LIB OFF)
|
set(BUILD_KERNEL_LIB OFF)
|
||||||
|
set(BUILD_KERNEL_TEST OFF)
|
||||||
set(BUILD_WALLET_TOOL OFF)
|
set(BUILD_WALLET_TOOL OFF)
|
||||||
set(BUILD_GUI OFF)
|
set(BUILD_GUI OFF)
|
||||||
set(ENABLE_EXTERNAL_SIGNER OFF)
|
set(ENABLE_EXTERNAL_SIGNER OFF)
|
||||||
|
@ -668,6 +670,7 @@ message(" bitcoin-util ........................ ${BUILD_UTIL}")
|
||||||
message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}")
|
message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}")
|
||||||
message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
|
message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
|
||||||
message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}")
|
message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}")
|
||||||
|
message(" kernel-test (experimental) .......... ${BUILD_KERNEL_TEST}")
|
||||||
message("Optional features:")
|
message("Optional features:")
|
||||||
message(" wallet support ...................... ${ENABLE_WALLET}")
|
message(" wallet support ...................... ${ENABLE_WALLET}")
|
||||||
message(" external signer ..................... ${ENABLE_EXTERNAL_SIGNER}")
|
message(" external signer ..................... ${ENABLE_EXTERNAL_SIGNER}")
|
||||||
|
|
|
@ -41,10 +41,10 @@ python3 --version
|
||||||
${CI_RETRY_EXE} pip3 install \
|
${CI_RETRY_EXE} pip3 install \
|
||||||
codespell==2.4.1 \
|
codespell==2.4.1 \
|
||||||
lief==0.16.6 \
|
lief==0.16.6 \
|
||||||
mypy==1.4.1 \
|
mypy==1.18.2 \
|
||||||
pyzmq==25.1.0 \
|
pyzmq==27.1.0 \
|
||||||
ruff==0.5.5 \
|
ruff==0.13.2 \
|
||||||
vulture==2.6
|
vulture==2.14
|
||||||
|
|
||||||
SHELLCHECK_VERSION=v0.11.0
|
SHELLCHECK_VERSION=v0.11.0
|
||||||
curl -sL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | \
|
curl -sL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | \
|
||||||
|
|
|
@ -12,7 +12,7 @@ export CONTAINER_NAME="ci_mac_native" # macos does not use a container, but the
|
||||||
export PIP_PACKAGES="--break-system-packages zmq"
|
export PIP_PACKAGES="--break-system-packages zmq"
|
||||||
export GOAL="install deploy"
|
export GOAL="install deploy"
|
||||||
export CMAKE_GENERATOR="Ninja"
|
export CMAKE_GENERATOR="Ninja"
|
||||||
export BITCOIN_CONFIG="-DBUILD_GUI=ON -DWITH_ZMQ=ON -DREDUCE_EXPORTS=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000'"
|
export BITCOIN_CONFIG="-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON -DREDUCE_EXPORTS=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000'"
|
||||||
export CI_OS_NAME="macos"
|
export CI_OS_NAME="macos"
|
||||||
export NO_DEPENDS=1
|
export NO_DEPENDS=1
|
||||||
export OSX_SDK=""
|
export OSX_SDK=""
|
||||||
|
|
|
@ -7,12 +7,15 @@
|
||||||
export LC_ALL=C.UTF-8
|
export LC_ALL=C.UTF-8
|
||||||
|
|
||||||
export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04"
|
export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04"
|
||||||
|
export APT_LLVM_V="21"
|
||||||
LIBCXX_DIR="/cxx_build/"
|
LIBCXX_DIR="/cxx_build/"
|
||||||
export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
|
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 MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
|
||||||
|
|
||||||
export CONTAINER_NAME="ci_native_fuzz_msan"
|
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 DEP_OPTS="DEBUG=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||||
export GOAL="all"
|
export GOAL="all"
|
||||||
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
|
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
|
||||||
|
|
|
@ -13,4 +13,4 @@ export PACKAGES="python3-zmq python3-pip clang-16 llvm-16 libc++abi-16-dev libc+
|
||||||
export PIP_PACKAGES="--break-system-packages pycapnp"
|
export PIP_PACKAGES="--break-system-packages pycapnp"
|
||||||
export DEP_OPTS="NO_WALLET=1 CC=clang-16 CXX='clang++-16 -stdlib=libc++'"
|
export DEP_OPTS="NO_WALLET=1 CC=clang-16 CXX='clang++-16 -stdlib=libc++'"
|
||||||
export GOAL="install"
|
export GOAL="install"
|
||||||
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON"
|
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON -DBUILD_SHARED_LIBS=ON"
|
||||||
|
|
|
@ -14,5 +14,5 @@ export PACKAGES="g++-mingw-w64-x86-64-posix nsis"
|
||||||
export RUN_UNIT_TESTS=false
|
export RUN_UNIT_TESTS=false
|
||||||
export RUN_FUNCTIONAL_TESTS=false
|
export RUN_FUNCTIONAL_TESTS=false
|
||||||
export GOAL="deploy"
|
export GOAL="deploy"
|
||||||
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_GUI_TESTS=OFF \
|
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_GUI_TESTS=OFF -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON \
|
||||||
-DCMAKE_CXX_FLAGS='-Wno-error=maybe-uninitialized'"
|
-DCMAKE_CXX_FLAGS='-Wno-error=maybe-uninitialized'"
|
||||||
|
|
|
@ -58,23 +58,6 @@ fi
|
||||||
if [[ -n "${USE_INSTRUMENTED_LIBCPP}" ]]; then
|
if [[ -n "${USE_INSTRUMENTED_LIBCPP}" ]]; then
|
||||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.1" /llvm-project
|
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.1" /llvm-project
|
||||||
|
|
||||||
if [ -n "${APT_LLVM_V}" ]; then
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
cmake -G Ninja -B /cxx_build/ \
|
cmake -G Ninja -B /cxx_build/ \
|
||||||
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
|
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
|
|
@ -130,7 +130,8 @@ if [[ "${RUN_TIDY}" == "true" ]]; then
|
||||||
BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
bash -c "cmake -S $BASE_ROOT_DIR -B ${BASE_BUILD_DIR} $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG" || (
|
eval "CMAKE_ARGS=($BITCOIN_CONFIG_ALL $BITCOIN_CONFIG)"
|
||||||
|
cmake -S "$BASE_ROOT_DIR" -B "$BASE_BUILD_DIR" "${CMAKE_ARGS[@]}" || (
|
||||||
cd "${BASE_BUILD_DIR}"
|
cd "${BASE_BUILD_DIR}"
|
||||||
# shellcheck disable=SC2046
|
# shellcheck disable=SC2046
|
||||||
cat $(cmake -P "${BASE_ROOT_DIR}/ci/test/GetCMakeLogFiles.cmake")
|
cat $(cmake -P "${BASE_ROOT_DIR}/ci/test/GetCMakeLogFiles.cmake")
|
||||||
|
|
|
@ -96,24 +96,24 @@ function(add_macos_deploy_target)
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
|
||||||
string(REPLACE " " "-" osx_volname ${CLIENT_NAME})
|
set(macos_zip "bitcoin-macos-app")
|
||||||
if(CMAKE_HOST_APPLE)
|
if(CMAKE_HOST_APPLE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_BINARY_DIR}/${osx_volname}.zip
|
OUTPUT ${PROJECT_BINARY_DIR}/${macos_zip}.zip
|
||||||
COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/contrib/macdeploy/macdeployqtplus ${macos_app} ${osx_volname} -translations-dir=${QT_TRANSLATIONS_DIR} -zip
|
COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/contrib/macdeploy/macdeployqtplus ${macos_app} -translations-dir=${QT_TRANSLATIONS_DIR} -zip=${macos_zip}
|
||||||
DEPENDS ${PROJECT_BINARY_DIR}/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
DEPENDS ${PROJECT_BINARY_DIR}/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
add_custom_target(deploydir
|
add_custom_target(deploydir
|
||||||
DEPENDS ${PROJECT_BINARY_DIR}/${osx_volname}.zip
|
DEPENDS ${PROJECT_BINARY_DIR}/${macos_zip}.zip
|
||||||
)
|
)
|
||||||
add_custom_target(deploy
|
add_custom_target(deploy
|
||||||
DEPENDS ${PROJECT_BINARY_DIR}/${osx_volname}.zip
|
DEPENDS ${PROJECT_BINARY_DIR}/${macos_zip}.zip
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_BINARY_DIR}/dist/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
OUTPUT ${PROJECT_BINARY_DIR}/dist/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
||||||
COMMAND ${CMAKE_COMMAND} -E env OBJDUMP=${CMAKE_OBJDUMP} $<TARGET_FILE:Python3::Interpreter> ${PROJECT_SOURCE_DIR}/contrib/macdeploy/macdeployqtplus ${macos_app} ${osx_volname} -translations-dir=${QT_TRANSLATIONS_DIR}
|
COMMAND ${CMAKE_COMMAND} -E env OBJDUMP=${CMAKE_OBJDUMP} $<TARGET_FILE:Python3::Interpreter> ${PROJECT_SOURCE_DIR}/contrib/macdeploy/macdeployqtplus ${macos_app} -translations-dir=${QT_TRANSLATIONS_DIR}
|
||||||
DEPENDS ${PROJECT_BINARY_DIR}/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
DEPENDS ${PROJECT_BINARY_DIR}/${macos_app}/Contents/MacOS/Bitcoin-Qt
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
@ -128,13 +128,13 @@ function(add_macos_deploy_target)
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_BINARY_DIR}/dist/${osx_volname}.zip
|
OUTPUT ${PROJECT_BINARY_DIR}/dist/${macos_zip}.zip
|
||||||
WORKING_DIRECTORY dist
|
WORKING_DIRECTORY dist
|
||||||
COMMAND ${PROJECT_SOURCE_DIR}/cmake/script/macos_zip.sh ${ZIP_EXECUTABLE} ${osx_volname}.zip
|
COMMAND ${PROJECT_SOURCE_DIR}/cmake/script/macos_zip.sh ${ZIP_EXECUTABLE} ${macos_zip}.zip
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
add_custom_target(deploy
|
add_custom_target(deploy
|
||||||
DEPENDS ${PROJECT_BINARY_DIR}/dist/${osx_volname}.zip
|
DEPENDS ${PROJECT_BINARY_DIR}/dist/${macos_zip}.zip
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or https://opensource.org/license/mit/.
|
# file COPYING or https://opensource.org/license/mit/.
|
||||||
|
|
||||||
|
enable_language(C)
|
||||||
|
|
||||||
function(add_secp256k1 subdir)
|
function(add_secp256k1 subdir)
|
||||||
message("")
|
message("")
|
||||||
message("Configuring secp256k1 subtree...")
|
message("Configuring secp256k1 subtree...")
|
||||||
|
@ -30,7 +32,6 @@ function(add_secp256k1 subdir)
|
||||||
string(STRIP "${SECP256K1_APPEND_LDFLAGS} ${APPEND_LDFLAGS}" SECP256K1_APPEND_LDFLAGS)
|
string(STRIP "${SECP256K1_APPEND_LDFLAGS} ${APPEND_LDFLAGS}" SECP256K1_APPEND_LDFLAGS)
|
||||||
set(SECP256K1_APPEND_LDFLAGS ${SECP256K1_APPEND_LDFLAGS} CACHE STRING "" FORCE)
|
set(SECP256K1_APPEND_LDFLAGS ${SECP256K1_APPEND_LDFLAGS} CACHE STRING "" FORCE)
|
||||||
# We want to build libsecp256k1 with the most tested RelWithDebInfo configuration.
|
# We want to build libsecp256k1 with the most tested RelWithDebInfo configuration.
|
||||||
enable_language(C)
|
|
||||||
foreach(config IN LISTS CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES)
|
foreach(config IN LISTS CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES)
|
||||||
if(config STREQUAL "")
|
if(config STREQUAL "")
|
||||||
continue()
|
continue()
|
||||||
|
|
|
@ -369,7 +369,7 @@ mkdir -p "$DISTSRC"
|
||||||
;;
|
;;
|
||||||
*darwin*)
|
*darwin*)
|
||||||
cmake --build build --target deploy ${V:+--verbose}
|
cmake --build build --target deploy ${V:+--verbose}
|
||||||
mv build/dist/Bitcoin-Core.zip "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.zip"
|
mv build/dist/bitcoin-macos-app.zip "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.zip"
|
||||||
mkdir -p "unsigned-app-${HOST}"
|
mkdir -p "unsigned-app-${HOST}"
|
||||||
cp --target-directory="unsigned-app-${HOST}" \
|
cp --target-directory="unsigned-app-${HOST}" \
|
||||||
contrib/macdeploy/detached-sig-create.sh
|
contrib/macdeploy/detached-sig-create.sh
|
||||||
|
|
|
@ -112,7 +112,6 @@ ELF_ALLOWED_LIBRARIES = {
|
||||||
'libfontconfig.so.1', # font support
|
'libfontconfig.so.1', # font support
|
||||||
'libfreetype.so.6', # font parsing
|
'libfreetype.so.6', # font parsing
|
||||||
'libdl.so.2', # programming interface to dynamic linker
|
'libdl.so.2', # programming interface to dynamic linker
|
||||||
'libxcb-cursor.so.0',
|
|
||||||
'libxcb-icccm.so.4',
|
'libxcb-icccm.so.4',
|
||||||
'libxcb-image.so.0',
|
'libxcb-image.so.0',
|
||||||
'libxcb-shm.so.0',
|
'libxcb-shm.so.0',
|
||||||
|
@ -249,7 +248,7 @@ def check_MACHO_libraries(binary) -> bool:
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def check_MACHO_min_os(binary) -> bool:
|
def check_MACHO_min_os(binary) -> bool:
|
||||||
if binary.build_version.minos == [13,0,0]:
|
if binary.build_version.minos == [14,0,0]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -390,12 +390,11 @@ Note, that the "dist" folder will be deleted before deploying on each run.
|
||||||
Optionally, Qt translation files (.qm) can be added to the bundle.""")
|
Optionally, Qt translation files (.qm) can be added to the bundle.""")
|
||||||
|
|
||||||
ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed")
|
ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed")
|
||||||
ap.add_argument("appname", nargs=1, metavar="appname", help="name of the app being deployed")
|
|
||||||
ap.add_argument("-verbose", nargs="?", const=True, help="Output additional debugging information")
|
ap.add_argument("-verbose", nargs="?", const=True, help="Output additional debugging information")
|
||||||
ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment")
|
ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment")
|
||||||
ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries")
|
ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries")
|
||||||
ap.add_argument("-translations-dir", nargs=1, metavar="path", default=None, help="Path to Qt's translations. Base translations will automatically be added to the bundle's resources.")
|
ap.add_argument("-translations-dir", nargs=1, metavar="path", default=None, help="Path to Qt's translations. Base translations will automatically be added to the bundle's resources.")
|
||||||
ap.add_argument("-zip", nargs="?", const="", metavar="zip", help="create a .zip containing the app bundle")
|
ap.add_argument("-zip", nargs=1, metavar="zip", help="create a .zip containing the app bundle")
|
||||||
|
|
||||||
config = ap.parse_args()
|
config = ap.parse_args()
|
||||||
|
|
||||||
|
@ -404,7 +403,6 @@ verbose = config.verbose
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
app_bundle = config.app_bundle[0]
|
app_bundle = config.app_bundle[0]
|
||||||
appname = config.appname[0]
|
|
||||||
|
|
||||||
if not os.path.exists(app_bundle):
|
if not os.path.exists(app_bundle):
|
||||||
sys.stderr.write(f"Error: Could not find app bundle \"{app_bundle}\"\n")
|
sys.stderr.write(f"Error: Could not find app bundle \"{app_bundle}\"\n")
|
||||||
|
@ -416,10 +414,6 @@ if os.path.exists("dist"):
|
||||||
print("+ Removing existing dist folder +")
|
print("+ Removing existing dist folder +")
|
||||||
shutil.rmtree("dist")
|
shutil.rmtree("dist")
|
||||||
|
|
||||||
if os.path.exists(appname + ".zip"):
|
|
||||||
print("+ Removing existing .zip +")
|
|
||||||
os.unlink(appname + ".zip")
|
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
target = os.path.join("dist", "Bitcoin-Qt.app")
|
target = os.path.join("dist", "Bitcoin-Qt.app")
|
||||||
|
@ -466,18 +460,18 @@ if config.translations_dir:
|
||||||
sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n")
|
sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n")
|
||||||
sys.exit(1)
|
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:
|
for file in lang_files:
|
||||||
if verbose:
|
if verbose:
|
||||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
|
@ -499,7 +493,13 @@ if platform.system() == "Darwin":
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
if config.zip is not None:
|
if config.zip is not None:
|
||||||
shutil.make_archive('{}'.format(appname), format='zip', root_dir='dist', base_dir='Bitcoin-Qt.app')
|
name = config.zip[0]
|
||||||
|
|
||||||
|
if os.path.exists(name + ".zip"):
|
||||||
|
print("+ Removing existing .zip +")
|
||||||
|
os.unlink(name + ".zip")
|
||||||
|
|
||||||
|
shutil.make_archive('{}'.format(name), format='zip', root_dir='dist', base_dir='Bitcoin-Qt.app')
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,8 @@ define fetch_file_inner
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define fetch_file
|
define fetch_file
|
||||||
( test -f $$($(1)_source_dir)/$(4) || \
|
|
||||||
( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \
|
( $(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),$(3),$(4),$(5)))
|
||||||
endef
|
endef
|
||||||
|
|
||||||
# Shell script to create a source tarball in $(1)_source from local directory
|
# Shell script to create a source tarball in $(1)_source from local directory
|
||||||
|
@ -109,7 +108,7 @@ $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/
|
||||||
$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources)
|
$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources)
|
||||||
|
|
||||||
#stamps
|
#stamps
|
||||||
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash
|
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_version)-$($(1)_sha256_hash).hash
|
||||||
$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted
|
$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted
|
||||||
$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed
|
$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed
|
||||||
$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned
|
$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned
|
||||||
|
@ -247,7 +246,6 @@ endif
|
||||||
$($(1)_fetched):
|
$($(1)_fetched):
|
||||||
mkdir -p $$(@D) $(SOURCES_PATH)
|
mkdir -p $$(@D) $(SOURCES_PATH)
|
||||||
rm -f $$@
|
rm -f $$@
|
||||||
touch $$@
|
|
||||||
cd $$(@D); $($(1)_fetch_cmds)
|
cd $$(@D); $($(1)_fetch_cmds)
|
||||||
cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);)
|
cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);)
|
||||||
touch $$@
|
touch $$@
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
OSX_MIN_VERSION=13.0
|
OSX_MIN_VERSION=14.0
|
||||||
OSX_SDK_VERSION=14.0
|
OSX_SDK_VERSION=14.0
|
||||||
XCODE_VERSION=15.0
|
XCODE_VERSION=15.0
|
||||||
XCODE_BUILD_ID=15A240d
|
XCODE_BUILD_ID=15A240d
|
||||||
|
|
|
@ -6,7 +6,7 @@ $(package)_sha256_hash=0e9c5446dc6f3beb8af6ebfcc9e27bcc6da6fe2860f7fc07b99144dfa
|
||||||
$(package)_dependencies=libxcb libxcb_util_render libxcb_util_image
|
$(package)_dependencies=libxcb libxcb_util_render libxcb_util_image
|
||||||
|
|
||||||
define $(package)_set_vars
|
define $(package)_set_vars
|
||||||
$(package)_config_opts = --disable-static
|
$(package)_config_opts = --disable-shared
|
||||||
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
|
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package=qrencode
|
package=qrencode
|
||||||
$(package)_version=4.1.1
|
$(package)_version=4.1.1
|
||||||
$(package)_download_path=https://fukuchi.org/works/qrencode/
|
$(package)_download_path=https://github.com/fukuchi/libqrencode/archive/refs/tags/
|
||||||
|
$(package)_download_file=v$($(package)_version).tar.gz
|
||||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||||
$(package)_sha256_hash=da448ed4f52aba6bcb0cd48cac0dd51b8692bccc4cd127431402fca6f8171e8e
|
$(package)_sha256_hash=5385bc1b8c2f20f3b91d258bf8ccc8cf62023935df2d2676b5b67049f31a049c
|
||||||
$(package)_patches=cmake_fixups.patch
|
$(package)_patches=cmake_fixups.patch
|
||||||
|
|
||||||
define $(package)_set_vars
|
define $(package)_set_vars
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package=systemtap
|
package=systemtap
|
||||||
$(package)_version=4.8
|
$(package)_version=5.3
|
||||||
$(package)_download_path=https://sourceware.org/ftp/systemtap/releases/
|
$(package)_download_path=https://sourceware.org/ftp/systemtap/releases/
|
||||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||||
$(package)_sha256_hash=cbd50a4eba5b261394dc454c12448ddec73e55e6742fda7f508f9fbc1331c223
|
$(package)_sha256_hash=966a360fb73a4b65a8d0b51b389577b3c4f92a327e84aae58682103e8c65a69a
|
||||||
$(package)_patches=remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch
|
$(package)_patches=remove_SDT_ASM_SECTION_AUTOGROUP_SUPPORT_check.patch
|
||||||
|
|
||||||
define $(package)_preprocess_cmds
|
define $(package)_preprocess_cmds
|
||||||
|
|
|
@ -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
|
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:
|
Additionally, to support Wayland protocol for modern desktop environments:
|
||||||
|
|
||||||
sudo apt install qt6-wayland
|
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
|
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:
|
Additionally, to support Wayland protocol for modern desktop environments:
|
||||||
|
|
||||||
sudo dnf install qt6-qtwayland
|
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
|
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:
|
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
|
apk add libqrencode-dev
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
| *libbitcoin_crypto* | Hardware-optimized functions for data encryption, hashing, message authentication, and key derivation. |
|
| *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*. |
|
| *libbitcoin_kernel* | Consensus engine and support library used for validation by *libbitcoin_node*. |
|
||||||
| *libbitcoinqt* | GUI functionality used by *bitcoin-qt* and *bitcoin-gui* executables. |
|
| *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_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_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. |
|
| *libbitcoin_wallet* | Wallet functionality used by *bitcoind* and *bitcoin-wallet* executables. |
|
||||||
|
|
|
@ -36,7 +36,7 @@ Compatibility
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Bitcoin Core is supported and tested on operating systems using the
|
Bitcoin Core is supported and tested on operating systems using the
|
||||||
Linux Kernel 3.17+, macOS 13+, and Windows 10+. Bitcoin
|
Linux Kernel 3.17+, macOS 14+, and Windows 10+. Bitcoin
|
||||||
Core should also work on most other Unix-like systems but is not as
|
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
|
frequently tested on them. It is not recommended to use Bitcoin Core on
|
||||||
unsupported systems.
|
unsupported systems.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<plist version="0.9">
|
<plist version="0.9">
|
||||||
<dict>
|
<dict>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>13</string>
|
<string>14</string>
|
||||||
|
|
||||||
<key>LSArchitecturePriority</key>
|
<key>LSArchitecturePriority</key>
|
||||||
<array>
|
<array>
|
||||||
|
|
|
@ -113,6 +113,19 @@ Section -post SEC0001
|
||||||
WriteRegStr HKCR "@CLIENT_TARNAME@" "" "URL:Bitcoin"
|
WriteRegStr HKCR "@CLIENT_TARNAME@" "" "URL:Bitcoin"
|
||||||
WriteRegStr HKCR "@CLIENT_TARNAME@\DefaultIcon" "" $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
|
WriteRegStr HKCR "@CLIENT_TARNAME@\DefaultIcon" "" $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
|
||||||
WriteRegStr HKCR "@CLIENT_TARNAME@\shell\open\command" "" '"$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" "%1"'
|
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
|
SectionEnd
|
||||||
|
|
||||||
# Macro for selecting uninstaller sections
|
# Macro for selecting uninstaller sections
|
||||||
|
|
|
@ -292,8 +292,8 @@ if(BUILD_BITCOIN_BIN)
|
||||||
add_executable(bitcoin bitcoin.cpp)
|
add_executable(bitcoin bitcoin.cpp)
|
||||||
add_windows_resources(bitcoin bitcoin-res.rc)
|
add_windows_resources(bitcoin bitcoin-res.rc)
|
||||||
add_windows_application_manifest(bitcoin)
|
add_windows_application_manifest(bitcoin)
|
||||||
target_link_libraries(bitcoin core_interface bitcoin_util)
|
target_link_libraries(bitcoin core_interface bitcoin_common bitcoin_util)
|
||||||
install_binary_component(bitcoin)
|
install_binary_component(bitcoin HAS_MANPAGE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Bitcoin Core bitcoind.
|
# Bitcoin Core bitcoind.
|
||||||
|
@ -404,6 +404,9 @@ endif()
|
||||||
|
|
||||||
if(BUILD_KERNEL_LIB)
|
if(BUILD_KERNEL_LIB)
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
|
if (BUILD_KERNEL_TEST)
|
||||||
|
add_subdirectory(test/kernel)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_UTIL_CHAINSTATE)
|
if(BUILD_UTIL_CHAINSTATE)
|
||||||
|
|
|
@ -24,7 +24,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
|
||||||
{
|
{
|
||||||
ECC_Context ecc_context{};
|
ECC_Context ecc_context{};
|
||||||
|
|
||||||
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||||
const int witnessversion = 0;
|
const int witnessversion = 0;
|
||||||
|
|
||||||
// Key pair.
|
// Key pair.
|
||||||
|
|
|
@ -1,53 +1,144 @@
|
||||||
// Copyright (c) 2022 The Bitcoin Core developers
|
#include <kernel/bitcoinkernel_wrapper.h>
|
||||||
// Distributed under the MIT software license, see the accompanying
|
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
||||||
//
|
|
||||||
// The bitcoin-chainstate executable serves to surface the dependencies required
|
|
||||||
// by a program wishing to use Bitcoin Core's consensus engine as it is right
|
|
||||||
// now.
|
|
||||||
//
|
|
||||||
// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
|
|
||||||
// it may diverge from Bitcoin Core's coding style.
|
|
||||||
//
|
|
||||||
// It is part of the libbitcoinkernel project.
|
|
||||||
|
|
||||||
#include <kernel/chainparams.h>
|
|
||||||
#include <kernel/chainstatemanager_opts.h>
|
|
||||||
#include <kernel/checks.h>
|
|
||||||
#include <kernel/context.h>
|
|
||||||
#include <kernel/warning.h>
|
|
||||||
|
|
||||||
#include <consensus/validation.h>
|
|
||||||
#include <core_io.h>
|
|
||||||
#include <kernel/caches.h>
|
|
||||||
#include <logging.h>
|
|
||||||
#include <node/blockstorage.h>
|
|
||||||
#include <node/chainstate.h>
|
|
||||||
#include <random.h>
|
|
||||||
#include <script/sigcache.h>
|
|
||||||
#include <util/chaintype.h>
|
|
||||||
#include <util/fs.h>
|
|
||||||
#include <util/signalinterrupt.h>
|
|
||||||
#include <util/task_runner.h>
|
|
||||||
#include <util/translation.h>
|
|
||||||
#include <validation.h>
|
|
||||||
#include <validationinterface.h>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <charconv>
|
||||||
#include <functional>
|
#include <filesystem>
|
||||||
#include <iosfwd>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// clang-format off
|
||||||
|
#include <windows.h>
|
||||||
|
// clang-format on
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace btck;
|
||||||
|
|
||||||
|
std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
|
||||||
|
{
|
||||||
|
std::vector<std::byte> bytes;
|
||||||
|
bytes.reserve(hex.length() / 2);
|
||||||
|
|
||||||
|
for (size_t i{0}; i < hex.length(); i += 2) {
|
||||||
|
uint8_t byte_value;
|
||||||
|
auto [ptr, ec] = std::from_chars(hex.data() + i, hex.data() + i + 2, byte_value, 16);
|
||||||
|
|
||||||
|
if (ec != std::errc{} || ptr != hex.data() + i + 2) {
|
||||||
|
throw std::invalid_argument("Invalid hex character");
|
||||||
|
}
|
||||||
|
bytes.push_back(static_cast<std::byte>(byte_value));
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
class KernelLog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void LogMessage(std::string_view message)
|
||||||
|
{
|
||||||
|
std::cout << "kernel: " << message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestValidationInterface : public ValidationInterface<TestValidationInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestValidationInterface() = default;
|
||||||
|
|
||||||
|
std::optional<std::string> m_expected_valid_block = std::nullopt;
|
||||||
|
|
||||||
|
void BlockChecked(const Block block, const BlockValidationState state) override
|
||||||
|
{
|
||||||
|
auto mode{state.GetValidationMode()};
|
||||||
|
switch (mode) {
|
||||||
|
case ValidationMode::VALID: {
|
||||||
|
std::cout << "Valid block" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ValidationMode::INVALID: {
|
||||||
|
std::cout << "Invalid block: ";
|
||||||
|
auto result{state.GetBlockValidationResult()};
|
||||||
|
switch (result) {
|
||||||
|
case BlockValidationResult::UNSET:
|
||||||
|
std::cout << "initial value. Block has not yet been rejected" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::HEADER_LOW_WORK:
|
||||||
|
std::cout << "the block header may be on a too-little-work chain" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::CONSENSUS:
|
||||||
|
std::cout << "invalid by consensus rules (excluding any below reasons)" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::CACHED_INVALID:
|
||||||
|
std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::INVALID_HEADER:
|
||||||
|
std::cout << "invalid proof of work or time too old" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::MUTATED:
|
||||||
|
std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::MISSING_PREV:
|
||||||
|
std::cout << "We don't have the previous block the checked one is built on" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::INVALID_PREV:
|
||||||
|
std::cout << "A block this one builds on is invalid" << std::endl;
|
||||||
|
break;
|
||||||
|
case BlockValidationResult::TIME_FUTURE:
|
||||||
|
std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ValidationMode::INTERNAL_ERROR: {
|
||||||
|
std::cout << "Internal error" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestKernelNotifications : public KernelNotifications<TestKernelNotifications>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void BlockTipHandler(SynchronizationState, const BlockTreeEntry, double) override
|
||||||
|
{
|
||||||
|
std::cout << "Block tip changed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) override
|
||||||
|
{
|
||||||
|
std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WarningSetHandler(Warning warning, std::string_view message) override
|
||||||
|
{
|
||||||
|
std::cout << message << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WarningUnsetHandler(Warning warning) override
|
||||||
|
{
|
||||||
|
std::cout << "Warning unset: " << static_cast<std::underlying_type_t<Warning>>(warning) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlushErrorHandler(std::string_view error) override
|
||||||
|
{
|
||||||
|
std::cout << error << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FatalErrorHandler(std::string_view error) override
|
||||||
|
{
|
||||||
|
std::cout << error << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
// We do not enable logging for this app, so explicitly disable it.
|
|
||||||
// To enable logging instead, replace with:
|
|
||||||
// LogInstance().m_print_to_console = true;
|
|
||||||
// LogInstance().StartLogging();
|
|
||||||
LogInstance().DisableLogging();
|
|
||||||
|
|
||||||
// SETUP: Argument parsing and handling
|
// SETUP: Argument parsing and handling
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
std::cerr
|
std::cerr
|
||||||
|
@ -58,213 +149,81 @@ int main(int argc, char* argv[])
|
||||||
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
|
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fs::path abs_datadir{fs::absolute(argv[1])};
|
|
||||||
fs::create_directories(abs_datadir);
|
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
int win_argc;
|
||||||
|
wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &win_argc);
|
||||||
|
std::vector<std::string> utf8_args(win_argc);
|
||||||
|
std::vector<char*> win_argv(win_argc);
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
|
||||||
|
for (int i = 0; i < win_argc; i++) {
|
||||||
|
utf8_args[i] = utf8_cvt.to_bytes(wargv[i]);
|
||||||
|
win_argv[i] = &utf8_args[i][0];
|
||||||
|
}
|
||||||
|
LocalFree(wargv);
|
||||||
|
argc = win_argc;
|
||||||
|
argv = win_argv.data();
|
||||||
|
#endif
|
||||||
|
|
||||||
// SETUP: Context
|
std::filesystem::path abs_datadir{std::filesystem::absolute(argv[1])};
|
||||||
kernel::Context kernel_context{};
|
std::filesystem::create_directories(abs_datadir);
|
||||||
// We can't use a goto here, but we can use an assert since none of the
|
|
||||||
// things instantiated so far requires running the epilogue to be torn down
|
|
||||||
// properly
|
|
||||||
assert(kernel::SanityChecks(kernel_context));
|
|
||||||
|
|
||||||
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
|
btck_LoggingOptions logging_options = {
|
||||||
|
.log_timestamps = true,
|
||||||
class KernelNotifications : public kernel::Notifications
|
.log_time_micros = false,
|
||||||
{
|
.log_threadnames = false,
|
||||||
public:
|
.log_sourcelocations = false,
|
||||||
kernel::InterruptResult blockTip(SynchronizationState, const CBlockIndex&, double) override
|
.always_print_category_levels = true,
|
||||||
{
|
|
||||||
std::cout << "Block tip changed" << std::endl;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override
|
|
||||||
{
|
|
||||||
std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl;
|
|
||||||
}
|
|
||||||
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
|
|
||||||
{
|
|
||||||
std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
|
|
||||||
}
|
|
||||||
void warningSet(kernel::Warning id, const bilingual_str& message) override
|
|
||||||
{
|
|
||||||
std::cout << "Warning " << static_cast<int>(id) << " set: " << message.original << std::endl;
|
|
||||||
}
|
|
||||||
void warningUnset(kernel::Warning id) override
|
|
||||||
{
|
|
||||||
std::cout << "Warning " << static_cast<int>(id) << " unset" << std::endl;
|
|
||||||
}
|
|
||||||
void flushError(const bilingual_str& message) override
|
|
||||||
{
|
|
||||||
std::cerr << "Error flushing block data to disk: " << message.original << std::endl;
|
|
||||||
}
|
|
||||||
void fatalError(const bilingual_str& message) override
|
|
||||||
{
|
|
||||||
std::cerr << "Error: " << message.original << std::endl;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
auto notifications = std::make_unique<KernelNotifications>();
|
|
||||||
|
|
||||||
kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE};
|
Logger logger{std::make_unique<KernelLog>(KernelLog{}), logging_options};
|
||||||
|
|
||||||
// SETUP: Chainstate
|
ContextOptions options{};
|
||||||
auto chainparams = CChainParams::Main();
|
ChainParams params{ChainType::MAINNET};
|
||||||
const ChainstateManager::Options chainman_opts{
|
options.SetChainParams(params);
|
||||||
.chainparams = *chainparams,
|
|
||||||
.datadir = abs_datadir,
|
|
||||||
.notifications = *notifications,
|
|
||||||
.signals = &validation_signals,
|
|
||||||
};
|
|
||||||
const node::BlockManager::Options blockman_opts{
|
|
||||||
.chainparams = chainman_opts.chainparams,
|
|
||||||
.blocks_dir = abs_datadir / "blocks",
|
|
||||||
.notifications = chainman_opts.notifications,
|
|
||||||
.block_tree_db_params = DBParams{
|
|
||||||
.path = abs_datadir / "blocks" / "index",
|
|
||||||
.cache_bytes = cache_sizes.block_tree_db,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
util::SignalInterrupt interrupt;
|
|
||||||
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
|
|
||||||
|
|
||||||
node::ChainstateLoadOptions options;
|
options.SetNotifications(std::make_shared<TestKernelNotifications>());
|
||||||
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
|
options.SetValidationInterface(std::make_shared<TestValidationInterface>());
|
||||||
if (status != node::ChainstateLoadStatus::SUCCESS) {
|
|
||||||
std::cerr << "Failed to load Chain state from your datadir." << std::endl;
|
Context context{options};
|
||||||
goto epilogue;
|
|
||||||
} else {
|
ChainstateManagerOptions chainman_opts{context, abs_datadir.string(), (abs_datadir / "blocks").string()};
|
||||||
std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
|
chainman_opts.SetWorkerThreads(4);
|
||||||
if (status != node::ChainstateLoadStatus::SUCCESS) {
|
|
||||||
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
|
std::unique_ptr<ChainMan> chainman;
|
||||||
goto epilogue;
|
try {
|
||||||
}
|
chainman = std::make_unique<ChainMan>(context, chainman_opts);
|
||||||
|
} catch (std::exception&) {
|
||||||
|
std::cerr << "Failed to instantiate ChainMan, exiting" << std::endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
|
std::cout << "Enter the block you want to validate on the next line:" << std::endl;
|
||||||
BlockValidationState state;
|
|
||||||
if (!chainstate->ActivateBestChain(state, nullptr)) {
|
|
||||||
std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
|
|
||||||
goto epilogue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main program logic starts here
|
|
||||||
std::cout
|
|
||||||
<< "Hello! I'm going to print out some information about your datadir." << std::endl
|
|
||||||
<< "\t"
|
|
||||||
<< "Path: " << abs_datadir << std::endl;
|
|
||||||
{
|
|
||||||
LOCK(chainman.GetMutex());
|
|
||||||
std::cout
|
|
||||||
<< "\t" << "Blockfiles Indexed: " << std::boolalpha << chainman.m_blockman.m_blockfiles_indexed.load() << std::noboolalpha << std::endl
|
|
||||||
<< "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
|
|
||||||
<< "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
|
|
||||||
<< "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
|
|
||||||
CBlockIndex* tip = chainman.ActiveTip();
|
|
||||||
if (tip) {
|
|
||||||
std::cout << "\t" << tip->ToString() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::string line; std::getline(std::cin, line);) {
|
for (std::string line; std::getline(std::cin, line);) {
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
std::cerr << "Empty line found" << std::endl;
|
std::cerr << "Empty line found, try again:" << std::endl;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
|
auto raw_block{hex_string_to_byte_vec(line)};
|
||||||
CBlock& block = *blockptr;
|
std::unique_ptr<Block> block;
|
||||||
|
try {
|
||||||
if (!DecodeHexBlk(block, line)) {
|
block = std::make_unique<Block>(raw_block);
|
||||||
std::cerr << "Block decode failed" << std::endl;
|
} catch (std::exception&) {
|
||||||
break;
|
std::cerr << "Block decode failed, try again:" << std::endl;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
bool new_block = false;
|
||||||
LOCK(cs_main);
|
bool accepted = chainman->ProcessBlock(*block, &new_block);
|
||||||
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
|
if (accepted) {
|
||||||
if (pindex) {
|
std::cerr << "Block has not yet been rejected" << std::endl;
|
||||||
chainman.UpdateUncommittedBlockStructures(block, pindex);
|
} else {
|
||||||
}
|
std::cerr << "Block was not accepted" << std::endl;
|
||||||
}
|
}
|
||||||
|
if (!new_block) {
|
||||||
// Adapted from rpc/mining.cpp
|
std::cerr << "Block is a duplicate" << std::endl;
|
||||||
class submitblock_StateCatcher final : public CValidationInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint256 hash;
|
|
||||||
bool found;
|
|
||||||
BlockValidationState state;
|
|
||||||
|
|
||||||
explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& stateIn) override
|
|
||||||
{
|
|
||||||
if (block->GetHash() != hash)
|
|
||||||
return;
|
|
||||||
found = true;
|
|
||||||
state = stateIn;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool new_block;
|
|
||||||
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
|
|
||||||
validation_signals.RegisterSharedValidationInterface(sc);
|
|
||||||
bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
|
|
||||||
validation_signals.UnregisterSharedValidationInterface(sc);
|
|
||||||
if (!new_block && accepted) {
|
|
||||||
std::cerr << "duplicate" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!sc->found) {
|
|
||||||
std::cerr << "inconclusive" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::cout << sc->state.ToString() << std::endl;
|
|
||||||
switch (sc->state.GetResult()) {
|
|
||||||
case BlockValidationResult::BLOCK_RESULT_UNSET:
|
|
||||||
std::cerr << "initial value. Block has not yet been rejected" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
|
|
||||||
std::cerr << "the block header may be on a too-little-work chain" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_CONSENSUS:
|
|
||||||
std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_CACHED_INVALID:
|
|
||||||
std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_INVALID_HEADER:
|
|
||||||
std::cerr << "invalid proof of work or time too old" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_MUTATED:
|
|
||||||
std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_MISSING_PREV:
|
|
||||||
std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_INVALID_PREV:
|
|
||||||
std::cerr << "A block this one builds on is invalid" << std::endl;
|
|
||||||
break;
|
|
||||||
case BlockValidationResult::BLOCK_TIME_FUTURE:
|
|
||||||
std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
epilogue:
|
|
||||||
// Without this precise shutdown sequence, there will be a lot of nullptr
|
|
||||||
// dereferencing and UB.
|
|
||||||
validation_signals.FlushBackgroundCallbacks();
|
|
||||||
{
|
|
||||||
LOCK(cs_main);
|
|
||||||
for (Chainstate* chainstate : chainman.GetAll()) {
|
|
||||||
if (chainstate->CanFlushToDisk()) {
|
|
||||||
chainstate->ForceFlushStateToDisk();
|
|
||||||
chainstate->ResetCoinsViews();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||||
|
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
|
#include <common/args.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/exec.h>
|
#include <util/exec.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
@ -47,7 +48,7 @@ Run '%s help' to see additional commands (e.g. for testing and debugging).
|
||||||
)";
|
)";
|
||||||
|
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
bool use_multiprocess{false};
|
std::optional<bool> use_multiprocess;
|
||||||
bool show_version{false};
|
bool show_version{false};
|
||||||
bool show_help{false};
|
bool show_help{false};
|
||||||
std::string_view command;
|
std::string_view command;
|
||||||
|
@ -55,6 +56,7 @@ struct CommandLine {
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandLine ParseCommandLine(int argc, char* argv[]);
|
CommandLine ParseCommandLine(int argc, char* argv[]);
|
||||||
|
bool UseMultiprocess(const CommandLine& cmd);
|
||||||
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
|
@ -78,9 +80,9 @@ int main(int argc, char* argv[])
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
} else if (cmd.command == "gui") {
|
} 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") {
|
} 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") {
|
} else if (cmd.command == "rpc") {
|
||||||
args.emplace_back("bitcoin-cli");
|
args.emplace_back("bitcoin-cli");
|
||||||
// Since "bitcoin rpc" is a new interface that doesn't need to be
|
// 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;
|
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`
|
//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
|
||||||
//! using src, bin and libexec directory paths relative to this executable, where
|
//! using src, bin and libexec directory paths relative to this executable, where
|
||||||
//! the path to this executable is specified in `wrapper_argv0`.
|
//! 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ProcessInitCommands(ArgsManager& args)
|
static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args)
|
||||||
{
|
{
|
||||||
// Process help and version before taking care about datadir
|
// Process help and version before taking care about datadir
|
||||||
if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
|
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)) {
|
if (args.GetBoolArg("-version", false)) {
|
||||||
strUsage += FormatParagraph(LicenseInfo());
|
strUsage += FormatParagraph(LicenseInfo());
|
||||||
|
@ -277,7 +282,7 @@ MAIN_FUNCTION
|
||||||
ArgsManager& args = *Assert(node.args);
|
ArgsManager& args = *Assert(node.args);
|
||||||
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
|
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
|
||||||
// Process early info return commands such as -help or -version
|
// 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
|
// Start application
|
||||||
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
|
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <chain.h>
|
#include <chain.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
|
||||||
std::string CBlockFileInfo::ToString() const
|
std::string CBlockFileInfo::ToString() const
|
||||||
|
@ -158,18 +159,26 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr
|
||||||
/** Find the last common ancestor two blocks have.
|
/** Find the last common ancestor two blocks have.
|
||||||
* Both pa and pb must be non-nullptr. */
|
* Both pa and pb must be non-nullptr. */
|
||||||
const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
|
const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
|
||||||
|
// First rewind to the last common height (the forking point cannot be past one of the two).
|
||||||
if (pa->nHeight > pb->nHeight) {
|
if (pa->nHeight > pb->nHeight) {
|
||||||
pa = pa->GetAncestor(pb->nHeight);
|
pa = pa->GetAncestor(pb->nHeight);
|
||||||
} else if (pb->nHeight > pa->nHeight) {
|
} else if (pb->nHeight > pa->nHeight) {
|
||||||
pb = pb->GetAncestor(pa->nHeight);
|
pb = pb->GetAncestor(pa->nHeight);
|
||||||
}
|
}
|
||||||
|
while (pa != pb) {
|
||||||
while (pa != pb && pa && pb) {
|
// Jump back until pa and pb have a common "skip" ancestor.
|
||||||
|
while (pa->pskip != pb->pskip) {
|
||||||
|
// This logic relies on the property that equal-height blocks have equal-height skip
|
||||||
|
// pointers.
|
||||||
|
Assume(pa->nHeight == pb->nHeight);
|
||||||
|
Assume(pa->pskip->nHeight == pb->pskip->nHeight);
|
||||||
|
pa = pa->pskip;
|
||||||
|
pb = pb->pskip;
|
||||||
|
}
|
||||||
|
// At this point, pa and pb are different, but have equal pskip. The forking point lies in
|
||||||
|
// between pa/pb on the one end, and pa->pskip/pb->pskip on the other end.
|
||||||
pa = pa->pprev;
|
pa = pa->pprev;
|
||||||
pb = pb->pprev;
|
pb = pb->pprev;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eventually all chain branches meet at the genesis block.
|
|
||||||
assert(pa == pb);
|
|
||||||
return pa;
|
return pa;
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,13 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
|
||||||
return search->second.m_flags;
|
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
|
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::string m_network GUARDED_BY(cs_args);
|
||||||
std::set<std::string> m_network_only_args 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::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};
|
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
||||||
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
||||||
std::optional<fs::path> m_config_path 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 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;
|
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
|
* Get settings file path, or return false if read-write settings were
|
||||||
* disabled with -nosettings.
|
* disabled with -nosettings.
|
||||||
|
|
|
@ -11,19 +11,25 @@
|
||||||
#include <util/string.h>
|
#include <util/string.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifdef WIN32
|
||||||
#include <sys/stat.h>
|
|
||||||
#else
|
|
||||||
#include <compat/compat.h>
|
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
|
#include <compat/compat.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_MALLOPT_ARENA_MAX
|
#ifdef HAVE_MALLOPT_ARENA_MAX
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
@ -105,6 +111,22 @@ int GetNumCores()
|
||||||
return std::thread::hardware_concurrency();
|
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)
|
// Obtain the application startup time (used for uptime calculation)
|
||||||
int64_t GetStartupTime()
|
int64_t GetStartupTime()
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// Application startup time (used for uptime calculation)
|
// Application startup time (used for uptime calculation)
|
||||||
|
@ -29,4 +30,9 @@ void runCommand(const std::string& strCommand);
|
||||||
*/
|
*/
|
||||||
int GetNumCores();
|
int GetNumCores();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the total RAM available on the current system, if detectable.
|
||||||
|
*/
|
||||||
|
std::optional<size_t> GetTotalRAM();
|
||||||
|
|
||||||
#endif // BITCOIN_COMMON_SYSTEM_H
|
#endif // BITCOIN_COMMON_SYSTEM_H
|
||||||
|
|
|
@ -75,14 +75,6 @@ typedef unsigned int SOCKET;
|
||||||
typedef SSIZE_T ssize_t;
|
typedef SSIZE_T ssize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The type of the option value passed to getsockopt & setsockopt
|
|
||||||
// differs between Windows and non-Windows.
|
|
||||||
#ifndef WIN32
|
|
||||||
typedef void* sockopt_arg_type;
|
|
||||||
#else
|
|
||||||
typedef char* sockopt_arg_type;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Export main() and ensure working ASLR when using mingw-w64.
|
// Export main() and ensure working ASLR when using mingw-w64.
|
||||||
// Exporting a symbol will prevent the linker from stripping
|
// Exporting a symbol will prevent the linker from stripping
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#ifndef BITCOIN_CONSENSUS_PARAMS_H
|
#ifndef BITCOIN_CONSENSUS_PARAMS_H
|
||||||
#define BITCOIN_CONSENSUS_PARAMS_H
|
#define BITCOIN_CONSENSUS_PARAMS_H
|
||||||
|
|
||||||
|
#include <script/verify_flags.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -89,7 +90,7 @@ struct Params {
|
||||||
* - buried in the chain, and
|
* - buried in the chain, and
|
||||||
* - fail if the default script verify flags are applied.
|
* - fail if the default script verify flags are applied.
|
||||||
*/
|
*/
|
||||||
std::map<uint256, uint32_t> script_flag_exceptions;
|
std::map<uint256, script_verify_flags> script_flag_exceptions;
|
||||||
/** Block height and hash at which BIP34 becomes active */
|
/** Block height and hash at which BIP34 becomes active */
|
||||||
int BIP34Height;
|
int BIP34Height;
|
||||||
uint256 BIP34Hash;
|
uint256 BIP34Hash;
|
||||||
|
|
|
@ -140,7 +140,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
||||||
return nSigOps;
|
return nSigOps;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags)
|
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags)
|
||||||
{
|
{
|
||||||
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
|
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define BITCOIN_CONSENSUS_TX_VERIFY_H
|
#define BITCOIN_CONSENSUS_TX_VERIFY_H
|
||||||
|
|
||||||
#include <consensus/amount.h>
|
#include <consensus/amount.h>
|
||||||
|
#include <script/verify_flags.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -52,7 +53,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma
|
||||||
* @param[in] flags Script verification flags
|
* @param[in] flags Script verification flags
|
||||||
* @return Total signature operation cost of tx
|
* @return Total signature operation cost of tx
|
||||||
*/
|
*/
|
||||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags);
|
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if transaction is final and can be included in a block with the
|
* Check if transaction is final and can be included in a block with the
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
#include <consensus/params.h>
|
#include <consensus/params.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
struct VBDeploymentInfo {
|
struct VBDeploymentInfo {
|
||||||
/** Deployment name */
|
/** Deployment name */
|
||||||
|
|
|
@ -401,7 +401,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
|
||||||
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
|
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
|
||||||
evutil_socket_t fd = evhttp_bound_socket_get_fd(bind_handle);
|
evutil_socket_t fd = evhttp_bound_socket_get_fd(bind_handle);
|
||||||
int one = 1;
|
int one = 1;
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (sockopt_arg_type)&one, sizeof(one)) == SOCKET_ERROR) {
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&one), sizeof(one)) == SOCKET_ERROR) {
|
||||||
LogInfo("WARNING: Unable to set TCP_NODELAY on RPC server socket, continuing anyway\n");
|
LogInfo("WARNING: Unable to set TCP_NODELAY on RPC server socket, continuing anyway\n");
|
||||||
}
|
}
|
||||||
boundSockets.push_back(bind_handle);
|
boundSockets.push_back(bind_handle);
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace sam {
|
||||||
|
|
||||||
Session::Session(const fs::path& private_key_file,
|
Session::Session(const fs::path& private_key_file,
|
||||||
const Proxy& control_host,
|
const Proxy& control_host,
|
||||||
CThreadInterrupt* interrupt)
|
std::shared_ptr<CThreadInterrupt> interrupt)
|
||||||
: m_private_key_file{private_key_file},
|
: m_private_key_file{private_key_file},
|
||||||
m_control_host{control_host},
|
m_control_host{control_host},
|
||||||
m_interrupt{interrupt},
|
m_interrupt{interrupt},
|
||||||
|
@ -127,7 +127,7 @@ Session::Session(const fs::path& private_key_file,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
|
Session::Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt)
|
||||||
: m_control_host{control_host},
|
: m_control_host{control_host},
|
||||||
m_interrupt{interrupt},
|
m_interrupt{interrupt},
|
||||||
m_transient{true}
|
m_transient{true}
|
||||||
|
@ -162,7 +162,7 @@ bool Session::Accept(Connection& conn)
|
||||||
std::string errmsg;
|
std::string errmsg;
|
||||||
bool disconnect{false};
|
bool disconnect{false};
|
||||||
|
|
||||||
while (!*m_interrupt) {
|
while (!m_interrupt->interrupted()) {
|
||||||
Sock::Event occurred;
|
Sock::Event occurred;
|
||||||
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
|
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
|
||||||
errmsg = "wait on socket failed";
|
errmsg = "wait on socket failed";
|
||||||
|
@ -205,7 +205,7 @@ bool Session::Accept(Connection& conn)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*m_interrupt) {
|
if (m_interrupt->interrupted()) {
|
||||||
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
|
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
|
||||||
} else {
|
} else {
|
||||||
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
|
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
|
||||||
|
|
14
src/i2p.h
14
src/i2p.h
|
@ -63,13 +63,11 @@ public:
|
||||||
* private key will be generated and saved into the file.
|
* private key will be generated and saved into the file.
|
||||||
* @param[in] control_host Location of the SAM proxy.
|
* @param[in] control_host Location of the SAM proxy.
|
||||||
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
||||||
* possible and executing methods throw an exception. Notice: only a pointer to the
|
* possible and executing methods throw an exception.
|
||||||
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
|
|
||||||
* `Session` object.
|
|
||||||
*/
|
*/
|
||||||
Session(const fs::path& private_key_file,
|
Session(const fs::path& private_key_file,
|
||||||
const Proxy& control_host,
|
const Proxy& control_host,
|
||||||
CThreadInterrupt* interrupt);
|
std::shared_ptr<CThreadInterrupt> interrupt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a transient session which will generate its own I2P private key
|
* Construct a transient session which will generate its own I2P private key
|
||||||
|
@ -78,11 +76,9 @@ public:
|
||||||
* the session will be lazily created later when first used.
|
* the session will be lazily created later when first used.
|
||||||
* @param[in] control_host Location of the SAM proxy.
|
* @param[in] control_host Location of the SAM proxy.
|
||||||
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
||||||
* possible and executing methods throw an exception. Notice: only a pointer to the
|
* possible and executing methods throw an exception.
|
||||||
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
|
|
||||||
* `Session` object.
|
|
||||||
*/
|
*/
|
||||||
Session(const Proxy& control_host, CThreadInterrupt* interrupt);
|
Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the session, closing the internally used sockets. The sockets that have been
|
* Destroy the session, closing the internally used sockets. The sockets that have been
|
||||||
|
@ -235,7 +231,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Cease network activity when this is signaled.
|
* Cease network activity when this is signaled.
|
||||||
*/
|
*/
|
||||||
CThreadInterrupt* const m_interrupt;
|
const std::shared_ptr<CThreadInterrupt> m_interrupt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutex protecting the members that can be concurrently accessed.
|
* Mutex protecting the members that can be concurrently accessed.
|
||||||
|
|
|
@ -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("-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("-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("-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",
|
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)",
|
"are of this size or less, allowing multiple outputs (default: %u)",
|
||||||
MAX_OP_RETURN_RELAY),
|
MAX_OP_RETURN_RELAY),
|
||||||
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_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."));
|
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.
|
// 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")) {
|
if (args.IsArgSet("-maxorphantx")) {
|
||||||
InitWarning(_("Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration."));
|
InitWarning(_("Option '-maxorphantx' is set but no longer has any effect (see release notes). Please remove it from your configuration."));
|
||||||
|
@ -1767,6 +1763,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||||
// ********************************************************* Step 7: load block chain
|
// ********************************************************* Step 7: load block chain
|
||||||
|
|
||||||
// cache size calculations
|
// cache size calculations
|
||||||
|
node::LogOversizedDbCache(args);
|
||||||
const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
|
const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
|
||||||
|
|
||||||
LogInfo("Cache configuration:");
|
LogInfo("Cache configuration:");
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
// bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node
|
// bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node
|
||||||
// options and will start the node with those options.
|
// options and will start the node with those options.
|
||||||
bool canListenIpc() override { return true; }
|
bool canListenIpc() override { return true; }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext m_node;
|
node::NodeContext m_node;
|
||||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
||||||
bool canListenIpc() override { return true; }
|
bool canListenIpc() override { return true; }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext& m_node;
|
node::NodeContext& m_node;
|
||||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
namespace {
|
namespace {
|
||||||
|
const char* EXE_NAME = "bitcoin-qt";
|
||||||
|
|
||||||
class BitcoinQtInit : public interfaces::Init
|
class BitcoinQtInit : public interfaces::Init
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -32,6 +34,7 @@ public:
|
||||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||||
}
|
}
|
||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext m_node;
|
node::NodeContext m_node;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -18,6 +18,8 @@ using node::NodeContext;
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
namespace {
|
namespace {
|
||||||
|
const char* EXE_NAME = "bitcoind";
|
||||||
|
|
||||||
class BitcoindInit : public interfaces::Init
|
class BitcoindInit : public interfaces::Init
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -34,6 +36,7 @@ public:
|
||||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||||
}
|
}
|
||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
NodeContext& m_node;
|
NodeContext& m_node;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
|
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
|
||||||
virtual Ipc* ipc() { return nullptr; }
|
virtual Ipc* ipc() { return nullptr; }
|
||||||
virtual bool canListenIpc() { return false; }
|
virtual bool canListenIpc() { return false; }
|
||||||
|
virtual const char* exeName() { return nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Return implementation of Init interface for the node process. If the argv
|
//! Return implementation of Init interface for the node process. If the argv
|
||||||
|
|
|
@ -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
|
CI_DIR=build-llvm
|
||||||
NIX_ARGS=(--arg enableLibcxx true)
|
NIX_ARGS=(--arg enableLibcxx true)
|
||||||
export CXX=clang++
|
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=(
|
CMAKE_ARGS=(
|
||||||
-G Ninja
|
-G Ninja
|
||||||
-DMP_ENABLE_CLANG_TIDY=ON
|
-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
|
CI_DIR=build-olddeps
|
||||||
export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-error=array-bounds"
|
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)
|
BUILD_ARGS=(-k)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
CI_DESC="CI job running ThreadSanitizer"
|
CI_DESC="CI job running ThreadSanitizer"
|
||||||
CI_DIR=build-sanitize
|
CI_DIR=build-sanitize
|
||||||
export CXX=clang++
|
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=()
|
CMAKE_ARGS=()
|
||||||
BUILD_ARGS=(-k -j4)
|
BUILD_ARGS=(-k -j4)
|
||||||
BUILD_TARGETS=(mptest)
|
BUILD_TARGETS=(mptest)
|
||||||
|
|
|
@ -17,6 +17,21 @@ fi
|
||||||
|
|
||||||
[ -n "${CI_CLEAN-}" ] && rm -rf "${CI_DIR}"
|
[ -n "${CI_CLEAN-}" ] && rm -rf "${CI_DIR}"
|
||||||
|
|
||||||
cmake -B "$CI_DIR" "${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}"
|
cmake --version
|
||||||
cmake --build "$CI_DIR" -t "${BUILD_TARGETS[@]}" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}"
|
cmake_ver=$(cmake --version | awk '/version/{print $3; exit}')
|
||||||
ctest --test-dir "$CI_DIR" --output-on-failure
|
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
|
||||||
|
|
|
@ -66,8 +66,6 @@ struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>
|
||||||
ProxyClient(const ProxyClient&) = delete;
|
ProxyClient(const ProxyClient&) = delete;
|
||||||
~ProxyClient();
|
~ProxyClient();
|
||||||
|
|
||||||
void setDisconnectCallback(const std::function<void()>& fn);
|
|
||||||
|
|
||||||
//! Reference to callback function that is run if there is a sudden
|
//! Reference to callback function that is run if there is a sudden
|
||||||
//! disconnect and the Connection object is destroyed before this
|
//! disconnect and the Connection object is destroyed before this
|
||||||
//! ProxyClient<Thread> object. The callback will destroy this object and
|
//! ProxyClient<Thread> object. The callback will destroy this object and
|
||||||
|
@ -285,16 +283,16 @@ struct Waiter
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void post(Fn&& fn)
|
void post(Fn&& fn)
|
||||||
{
|
{
|
||||||
const std::unique_lock<std::mutex> lock(m_mutex);
|
const Lock lock(m_mutex);
|
||||||
assert(!m_fn);
|
assert(!m_fn);
|
||||||
m_fn = std::forward<Fn>(fn);
|
m_fn = std::forward<Fn>(fn);
|
||||||
m_cv.notify_all();
|
m_cv.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Predicate>
|
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
|
// 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
|
// 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
|
// after the fn() call and before the lock.lock() call in this loop
|
||||||
|
@ -317,9 +315,9 @@ struct Waiter
|
||||||
//! mutexes than necessary. This mutex can be held at the same time as
|
//! 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 as long as Waiter::mutex is locked first and
|
||||||
//! EventLoop::m_mutex is locked second.
|
//! EventLoop::m_mutex is locked second.
|
||||||
std::mutex m_mutex;
|
Mutex m_mutex;
|
||||||
std::condition_variable m_cv;
|
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
|
//! Object holding network & rpc state associated with either an incoming server
|
||||||
|
@ -544,29 +542,73 @@ void ProxyServerBase<Interface, Impl>::invokeDestroy()
|
||||||
CleanupRun(m_context.cleanup_fns);
|
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;
|
using ConnThread = ConnThreads::iterator;
|
||||||
|
|
||||||
// Retrieve ProxyClient<Thread> object associated with this connection from a
|
// 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
|
// map, or create a new one and insert it into the map. Return map iterator and
|
||||||
// inserted bool.
|
// 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
|
struct ThreadContext
|
||||||
{
|
{
|
||||||
//! Identifying string for debug.
|
//! Identifying string for debug.
|
||||||
std::string thread_name;
|
std::string thread_name;
|
||||||
|
|
||||||
//! Waiter object used to allow client threads blocked waiting for a server
|
//! Waiter object used to allow remote clients to execute code on this
|
||||||
//! response to execute callbacks made from the client's corresponding
|
//! thread. For server threads created by
|
||||||
//! server thread.
|
//! 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;
|
std::unique_ptr<Waiter> waiter = nullptr;
|
||||||
|
|
||||||
//! When client is making a request to a server, this is the
|
//! When client is making a request to a server, this is the
|
||||||
//! `callbackThread` argument it passes in the request, used by the server
|
//! `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
|
//! 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.
|
//! 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`
|
//! 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
|
//! argument it passes in the request, used to control which thread on
|
||||||
|
@ -575,7 +617,9 @@ struct ThreadContext
|
||||||
//! by makeThread. If a client call is being made from a thread currently
|
//! by makeThread. If a client call is being made from a thread currently
|
||||||
//! handling a server request, this will be set to the `callbackThread`
|
//! handling a server request, this will be set to the `callbackThread`
|
||||||
//! request thread argument passed in that request.
|
//! 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
|
//! 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
|
//! to assert false if there's an attempt to execute a blocking operation
|
||||||
|
|
|
@ -617,7 +617,7 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||||
const char* disconnected = nullptr;
|
const char* disconnected = nullptr;
|
||||||
proxy_client.m_context.loop->sync([&]() {
|
proxy_client.m_context.loop->sync([&]() {
|
||||||
if (!proxy_client.m_context.connection) {
|
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;
|
done = true;
|
||||||
disconnected = "IPC client method called after disconnect.";
|
disconnected = "IPC client method called after disconnect.";
|
||||||
thread_context.waiter->m_cv.notify_all();
|
thread_context.waiter->m_cv.notify_all();
|
||||||
|
@ -644,7 +644,7 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
exception = std::current_exception();
|
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;
|
done = true;
|
||||||
thread_context.waiter->m_cv.notify_all();
|
thread_context.waiter->m_cv.notify_all();
|
||||||
},
|
},
|
||||||
|
@ -656,13 +656,13 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
|
||||||
proxy_client.m_context.loop->logPlain()
|
proxy_client.m_context.loop->logPlain()
|
||||||
<< "{" << thread_context.thread_name << "} IPC client exception " << kj_exception;
|
<< "{" << 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;
|
done = true;
|
||||||
thread_context.waiter->m_cv.notify_all();
|
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; });
|
thread_context.waiter->wait(lock, [&done]() { return done; });
|
||||||
if (exception) std::rethrow_exception(exception);
|
if (exception) std::rethrow_exception(exception);
|
||||||
if (!kj_exception.empty()) proxy_client.m_context.loop->raise() << kj_exception;
|
if (!kj_exception.empty()) proxy_client.m_context.loop->raise() << kj_exception;
|
||||||
|
|
|
@ -25,7 +25,7 @@ void CustomBuildField(TypeList<>,
|
||||||
// Also store the Thread::Client reference in the callback_threads map so
|
// Also store the Thread::Client reference in the callback_threads map so
|
||||||
// future calls over this connection can reuse it.
|
// future calls over this connection can reuse it.
|
||||||
auto [callback_thread, _]{SetThread(
|
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{})); })};
|
[&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(thread_context, std::thread{})); })};
|
||||||
|
|
||||||
// Call remote ThreadMap.makeThread function so server will create a
|
// 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.
|
return request.send().getResult(); // Nonblocking due to capnp request pipelining.
|
||||||
}};
|
}};
|
||||||
auto [request_thread, _1]{SetThread(
|
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)};
|
&connection, make_request_thread)};
|
||||||
|
|
||||||
auto context = output.init();
|
auto context = output.init();
|
||||||
context.setThread(request_thread->second.m_client);
|
context.setThread(request_thread->second->m_client);
|
||||||
context.setCallbackThread(callback_thread->second.m_client);
|
context.setCallbackThread(callback_thread->second->m_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! PassField override for mp.Context arguments. Return asynchronously and call
|
//! 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.
|
// need to update the map.
|
||||||
auto& thread_context = g_thread_context;
|
auto& thread_context = g_thread_context;
|
||||||
auto& request_threads = thread_context.request_threads;
|
auto& request_threads = thread_context.request_threads;
|
||||||
auto [request_thread, inserted]{SetThread(
|
ConnThread request_thread;
|
||||||
request_threads, thread_context.waiter->m_mutex,
|
bool inserted;
|
||||||
server.m_context.connection,
|
server.m_context.loop->sync([&] {
|
||||||
[&] { return context_arg.getCallbackThread(); })};
|
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
|
// remove it after calling fn.invoke. If an entry was not
|
||||||
// inserted, one already existed, meaning this must be a
|
// inserted, one already existed, meaning this must be a
|
||||||
// recursive call (IPC call calling back to the caller which
|
// recursive call (IPC call calling back to the caller which
|
||||||
// makes another IPC call), so avoid modifying the map.
|
// makes another IPC call), so avoid modifying the map.
|
||||||
const bool erase_thread{inserted};
|
const bool erase_thread{inserted};
|
||||||
KJ_DEFER(if (erase_thread) {
|
KJ_DEFER(if (erase_thread) {
|
||||||
std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
|
// Erase the request_threads entry on the event loop
|
||||||
// Call erase here with a Connection* argument instead
|
// thread with loop->sync(), so if the connection is
|
||||||
// of an iterator argument, because the `request_thread`
|
// broken there is not a race between this thread and
|
||||||
// iterator may be invalid if the connection is closed
|
// the disconnect handler trying to destroy the thread
|
||||||
// during this function call. More specifically, the
|
// client object.
|
||||||
// iterator may be invalid because SetThread adds a
|
server.m_context.loop->sync([&] {
|
||||||
// cleanup callback to the Connection destructor that
|
// Look up the thread again without using existing
|
||||||
// erases the thread from the map, and also because the
|
// iterator since entry may no longer be there after
|
||||||
// ProxyServer<Thread> destructor calls
|
// a disconnect. Destroy node after releasing
|
||||||
// request_threads.clear().
|
// Waiter::m_mutex, so the ProxyClient<Thread>
|
||||||
request_threads.erase(server.m_context.connection);
|
// 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...);
|
fn.invoke(server_context, args...);
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,17 @@ public:
|
||||||
std::unique_lock<std::mutex> m_lock;
|
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.
|
//! Analog to std::lock_guard that unlocks instead of locks.
|
||||||
template <typename Lock>
|
template <typename Lock>
|
||||||
struct UnlockGuard
|
struct UnlockGuard
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
, enableLibcxx ? false # Whether to use libc++ toolchain and libraries instead of libstdc++
|
, 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)
|
, minimal ? false # Whether to create minimal shell without extra tools (faster when cross compiling)
|
||||||
, capnprotoVersion ? null
|
, capnprotoVersion ? null
|
||||||
|
, cmakeVersion ? null
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -37,12 +38,23 @@ let
|
||||||
capnproto = capnprotoBase.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; });
|
capnproto = capnprotoBase.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; });
|
||||||
clang = if enableLibcxx then llvm.libcxxClang else llvm.clang;
|
clang = if enableLibcxx then llvm.libcxxClang else llvm.clang;
|
||||||
clang-tools = llvm.clang-tools.override { inherit enableLibcxx; };
|
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 {
|
in crossPkgs.mkShell {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
capnproto
|
capnproto
|
||||||
];
|
];
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
cmake
|
cmakeBuild
|
||||||
include-what-you-use
|
include-what-you-use
|
||||||
ninja
|
ninja
|
||||||
] ++ lib.optionals (!minimal) [
|
] ++ lib.optionals (!minimal) [
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <capnp/capability.h>
|
#include <capnp/capability.h>
|
||||||
|
#include <capnp/common.h> // IWYU pragma: keep
|
||||||
#include <capnp/rpc.h>
|
#include <capnp/rpc.h>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -25,7 +26,6 @@
|
||||||
#include <kj/memory.h>
|
#include <kj/memory.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -81,6 +81,11 @@ ProxyContext::ProxyContext(Connection* connection) : connection(connection), loo
|
||||||
|
|
||||||
Connection::~Connection()
|
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
|
// Shut down RPC system first, since this will garbage collect any
|
||||||
// ProxyServer objects that were not freed before the connection was closed.
|
// ProxyServer objects that were not freed before the connection was closed.
|
||||||
// Typically all ProxyServer objects associated with this connection will be
|
// Typically all ProxyServer objects associated with this connection will be
|
||||||
|
@ -156,6 +161,9 @@ CleanupIt Connection::addSyncCleanup(std::function<void()> fn)
|
||||||
|
|
||||||
void Connection::removeSyncCleanup(CleanupIt it)
|
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);
|
const Lock lock(m_loop->m_mutex);
|
||||||
m_sync_cleanup_fns.erase(it);
|
m_sync_cleanup_fns.erase(it);
|
||||||
}
|
}
|
||||||
|
@ -305,29 +313,34 @@ bool EventLoop::done() const
|
||||||
return m_num_clients == 0 && m_async_fns->empty();
|
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);
|
assert(std::this_thread::get_id() == connection->m_loop->m_thread_id);
|
||||||
auto thread = threads.find(connection);
|
ConnThread thread;
|
||||||
if (thread != threads.end()) return {thread, false};
|
bool inserted;
|
||||||
thread = threads.emplace(
|
{
|
||||||
std::piecewise_construct, std::forward_as_tuple(connection),
|
const Lock lock(threads.mutex);
|
||||||
std::forward_as_tuple(make_thread(), connection, /* destroy_connection= */ false)).first;
|
std::tie(thread, inserted) = threads.ref.try_emplace(connection);
|
||||||
thread->second.setDisconnectCallback([&threads, &mutex, thread] {
|
}
|
||||||
// Note: it is safe to use the `thread` iterator in this cleanup
|
if (inserted) {
|
||||||
// function, because the iterator would only be invalid if the map entry
|
thread->second.emplace(make_thread(), connection, /* destroy_connection= */ false);
|
||||||
// was removed, and if the map entry is removed the ProxyClient<Thread>
|
thread->second->m_disconnect_cb = connection->addSyncCleanup([threads, thread] {
|
||||||
// destructor unregisters the cleanup.
|
// 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
|
// Connection is being destroyed before thread client is, so reset
|
||||||
// thread client m_disconnect_cb member so thread client destructor does not
|
// thread client m_disconnect_cb member so thread client destructor does not
|
||||||
// try to unregister this callback after connection is destroyed.
|
// try to unregister this callback after connection is destroyed.
|
||||||
// Remove connection pointer about to be destroyed from the map
|
thread->second->m_disconnect_cb.reset();
|
||||||
const std::unique_lock<std::mutex> lock(mutex);
|
|
||||||
thread->second.m_disconnect_cb.reset();
|
// Remove connection pointer about to be destroyed from the map
|
||||||
threads.erase(thread);
|
const Lock lock(threads.mutex);
|
||||||
});
|
threads.ref.erase(thread);
|
||||||
return {thread, true};
|
});
|
||||||
|
}
|
||||||
|
return {thread, inserted};
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyClient<Thread>::~ProxyClient()
|
ProxyClient<Thread>::~ProxyClient()
|
||||||
|
@ -336,17 +349,18 @@ ProxyClient<Thread>::~ProxyClient()
|
||||||
// cleanup callback that was registered to handle the connection being
|
// cleanup callback that was registered to handle the connection being
|
||||||
// destroyed before the thread being destroyed.
|
// destroyed before the thread being destroyed.
|
||||||
if (m_disconnect_cb) {
|
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)
|
ProxyServer<Thread>::ProxyServer(ThreadContext& thread_context, std::thread&& thread)
|
||||||
: m_thread_context(thread_context), m_thread(std::move(thread))
|
: m_thread_context(thread_context), m_thread(std::move(thread))
|
||||||
{
|
{
|
||||||
|
@ -364,7 +378,7 @@ ProxyServer<Thread>::~ProxyServer()
|
||||||
assert(m_thread_context.waiter.get());
|
assert(m_thread_context.waiter.get());
|
||||||
std::unique_ptr<Waiter> waiter;
|
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
|
//! Reset thread context waiter pointer, as shutdown signal for done
|
||||||
//! lambda passed as waiter->wait() argument in makeThread code below.
|
//! lambda passed as waiter->wait() argument in makeThread code below.
|
||||||
waiter = std::move(m_thread_context.waiter);
|
waiter = std::move(m_thread_context.waiter);
|
||||||
|
@ -398,7 +412,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.thread_name = ThreadName(m_connection.m_loop->m_exe_name) + " (from " + from + ")";
|
||||||
g_thread_context.waiter = std::make_unique<Waiter>();
|
g_thread_context.waiter = std::make_unique<Waiter>();
|
||||||
thread_context.set_value(&g_thread_context);
|
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
|
// Wait for shutdown signal from ProxyServer<Thread> destructor (signal
|
||||||
// is just waiter getting set to null.)
|
// is just waiter getting set to null.)
|
||||||
g_thread_context.waiter->wait(lock, [] { return !g_thread_context.waiter; });
|
g_thread_context.waiter->wait(lock, [] { return !g_thread_context.waiter; });
|
||||||
|
|
|
@ -96,20 +96,15 @@ target_link_libraries(bitcoinkernel
|
||||||
|
|
||||||
target_include_directories(bitcoinkernel PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/leveldb/include>)
|
target_include_directories(bitcoinkernel PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/leveldb/include>)
|
||||||
|
|
||||||
# libbitcoinkernel requires default symbol visibility, explicitly
|
|
||||||
# specify that here so that things still work even when user
|
|
||||||
# configures with -DREDUCE_EXPORTS=ON
|
|
||||||
#
|
|
||||||
# Note this is a quick hack that will be removed as we
|
|
||||||
# incrementally define what to export from the library.
|
|
||||||
set_target_properties(bitcoinkernel PROPERTIES
|
|
||||||
CXX_VISIBILITY_PRESET default
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add a convenience libbitcoinkernel target as a synonym for bitcoinkernel.
|
# Add a convenience libbitcoinkernel target as a synonym for bitcoinkernel.
|
||||||
add_custom_target(libbitcoinkernel)
|
add_custom_target(libbitcoinkernel)
|
||||||
add_dependencies(libbitcoinkernel bitcoinkernel)
|
add_dependencies(libbitcoinkernel bitcoinkernel)
|
||||||
|
|
||||||
|
get_target_property(bitcoinkernel_type bitcoinkernel TYPE)
|
||||||
|
if(bitcoinkernel_type STREQUAL "STATIC_LIBRARY")
|
||||||
|
target_compile_definitions(bitcoinkernel PUBLIC BITCOINKERNEL_STATIC)
|
||||||
|
endif()
|
||||||
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/libbitcoinkernel.pc.in ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc @ONLY)
|
configure_file(${PROJECT_SOURCE_DIR}/libbitcoinkernel.pc.in ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc @ONLY)
|
||||||
install(FILES ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT libbitcoinkernel)
|
install(FILES ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT libbitcoinkernel)
|
||||||
|
|
||||||
|
@ -124,3 +119,5 @@ install(TARGETS bitcoinkernel
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
COMPONENT libbitcoinkernel
|
COMPONENT libbitcoinkernel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
install(FILES bitcoinkernel.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT libbitcoinkernel)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
20
src/key.cpp
20
src/key.cpp
|
@ -155,7 +155,7 @@ int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, si
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CKey::Check(const unsigned char *vch) {
|
bool CKey::Check(const unsigned char *vch) {
|
||||||
return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch);
|
return secp256k1_ec_seckey_verify(secp256k1_context_static, vch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CKey::MakeNewKey(bool fCompressedIn) {
|
void CKey::MakeNewKey(bool fCompressedIn) {
|
||||||
|
@ -186,7 +186,7 @@ CPubKey CKey::GetPubKey() const {
|
||||||
CPubKey result;
|
CPubKey result;
|
||||||
int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, UCharCast(begin()));
|
int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, UCharCast(begin()));
|
||||||
assert(ret);
|
assert(ret);
|
||||||
secp256k1_ec_pubkey_serialize(secp256k1_context_sign, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
|
secp256k1_ec_pubkey_serialize(secp256k1_context_static, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
|
||||||
assert(result.size() == clen);
|
assert(result.size() == clen);
|
||||||
assert(result.IsValid());
|
assert(result.IsValid());
|
||||||
return result;
|
return result;
|
||||||
|
@ -196,7 +196,7 @@ CPubKey CKey::GetPubKey() const {
|
||||||
bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
|
bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
|
||||||
{
|
{
|
||||||
unsigned char compact_sig[64];
|
unsigned char compact_sig[64];
|
||||||
secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_sign, compact_sig, sig);
|
secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_static, compact_sig, sig);
|
||||||
|
|
||||||
// In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates
|
// In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates
|
||||||
// its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted
|
// its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted
|
||||||
|
@ -222,7 +222,7 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool gr
|
||||||
ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, extra_entropy);
|
ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, extra_entropy);
|
||||||
}
|
}
|
||||||
assert(ret);
|
assert(ret);
|
||||||
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig);
|
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_static, vchSig.data(), &nSigLen, &sig);
|
||||||
vchSig.resize(nSigLen);
|
vchSig.resize(nSigLen);
|
||||||
// Additional verification step to prevent using a potentially corrupted signature
|
// Additional verification step to prevent using a potentially corrupted signature
|
||||||
secp256k1_pubkey pk;
|
secp256k1_pubkey pk;
|
||||||
|
@ -254,7 +254,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
|
||||||
secp256k1_ecdsa_recoverable_signature rsig;
|
secp256k1_ecdsa_recoverable_signature rsig;
|
||||||
int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, nullptr);
|
int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, nullptr);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &rsig);
|
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_static, &vchSig[1], &rec, &rsig);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
assert(rec != -1);
|
assert(rec != -1);
|
||||||
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
|
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
|
||||||
|
@ -277,7 +277,7 @@ bool CKey::SignSchnorr(const uint256& hash, std::span<unsigned char> sig, const
|
||||||
|
|
||||||
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
|
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
|
||||||
MakeKeyData();
|
MakeKeyData();
|
||||||
if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size())) {
|
if (!ec_seckey_import_der(secp256k1_context_static, (unsigned char*)begin(), seckey.data(), seckey.size())) {
|
||||||
ClearKeyData();
|
ClearKeyData();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +303,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
|
||||||
}
|
}
|
||||||
memcpy(ccChild.begin(), vout.data()+32, 32);
|
memcpy(ccChild.begin(), vout.data()+32, 32);
|
||||||
keyChild.Set(begin(), begin() + 32, true);
|
keyChild.Set(begin(), begin() + 32, true);
|
||||||
bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
|
bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_static, (unsigned char*)keyChild.begin(), vout.data());
|
||||||
if (!ret) keyChild.ClearKeyData();
|
if (!ret) keyChild.ClearKeyData();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,7 @@ ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, c
|
||||||
ECDHSecret output;
|
ECDHSecret output;
|
||||||
// BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs
|
// BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs
|
||||||
// accordingly:
|
// accordingly:
|
||||||
bool success = secp256k1_ellswift_xdh(secp256k1_context_sign,
|
bool success = secp256k1_ellswift_xdh(secp256k1_context_static,
|
||||||
UCharCast(output.data()),
|
UCharCast(output.data()),
|
||||||
UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()),
|
UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()),
|
||||||
UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()),
|
UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()),
|
||||||
|
@ -415,8 +415,8 @@ KeyPair::KeyPair(const CKey& key, const uint256* merkle_root)
|
||||||
if (success && merkle_root) {
|
if (success && merkle_root) {
|
||||||
secp256k1_xonly_pubkey pubkey;
|
secp256k1_xonly_pubkey pubkey;
|
||||||
unsigned char pubkey_bytes[32];
|
unsigned char pubkey_bytes[32];
|
||||||
assert(secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, keypair));
|
assert(secp256k1_keypair_xonly_pub(secp256k1_context_static, &pubkey, nullptr, keypair));
|
||||||
assert(secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey));
|
assert(secp256k1_xonly_pubkey_serialize(secp256k1_context_static, pubkey_bytes, &pubkey));
|
||||||
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
|
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
|
||||||
success = secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, keypair, tweak.data());
|
success = secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, keypair, tweak.data());
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,7 @@ static const std::map<std::string, BCLog::LogFlags, std::less<>> LOG_CATEGORIES_
|
||||||
{"txreconciliation", BCLog::TXRECONCILIATION},
|
{"txreconciliation", BCLog::TXRECONCILIATION},
|
||||||
{"scan", BCLog::SCAN},
|
{"scan", BCLog::SCAN},
|
||||||
{"txpackages", BCLog::TXPACKAGES},
|
{"txpackages", BCLog::TXPACKAGES},
|
||||||
|
{"kernel", BCLog::KERNEL},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
|
static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
|
||||||
|
|
|
@ -94,6 +94,7 @@ namespace BCLog {
|
||||||
TXRECONCILIATION = (CategoryMask{1} << 26),
|
TXRECONCILIATION = (CategoryMask{1} << 26),
|
||||||
SCAN = (CategoryMask{1} << 27),
|
SCAN = (CategoryMask{1} << 27),
|
||||||
TXPACKAGES = (CategoryMask{1} << 28),
|
TXPACKAGES = (CategoryMask{1} << 28),
|
||||||
|
KERNEL = (CategoryMask{1} << 29),
|
||||||
ALL = ~NONE,
|
ALL = ~NONE,
|
||||||
};
|
};
|
||||||
enum class Level {
|
enum class Level {
|
||||||
|
@ -256,6 +257,12 @@ namespace BCLog {
|
||||||
m_print_callbacks.erase(it);
|
m_print_callbacks.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t NumConnections()
|
||||||
|
{
|
||||||
|
StdLockGuard scoped_lock(m_cs);
|
||||||
|
return m_print_callbacks.size();
|
||||||
|
}
|
||||||
|
|
||||||
/** Start logging (and flush all buffered messages) */
|
/** Start logging (and flush all buffered messages) */
|
||||||
bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
|
bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
|
||||||
/** Only for testing */
|
/** Only for testing */
|
||||||
|
@ -287,6 +294,11 @@ namespace BCLog {
|
||||||
StdLockGuard scoped_lock(m_cs);
|
StdLockGuard scoped_lock(m_cs);
|
||||||
m_category_log_levels = levels;
|
m_category_log_levels = levels;
|
||||||
}
|
}
|
||||||
|
void AddCategoryLogLevel(LogFlags category, Level level)
|
||||||
|
{
|
||||||
|
StdLockGuard scoped_lock(m_cs);
|
||||||
|
m_category_log_levels[category] = level;
|
||||||
|
}
|
||||||
bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
|
bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
|
||||||
|
|
||||||
Level LogLevel() const { return m_log_level.load(); }
|
Level LogLevel() const { return m_log_level.load(); }
|
||||||
|
|
209
src/net.cpp
209
src/net.cpp
|
@ -108,7 +108,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
|
||||||
|
|
||||||
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
||||||
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
||||||
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
|
||||||
//
|
//
|
||||||
// Global state variables
|
// Global state variables
|
||||||
//
|
//
|
||||||
|
@ -331,42 +331,22 @@ bool IsLocal(const CService& addr)
|
||||||
return mapLocalHost.count(addr) > 0;
|
return mapLocalHost.count(addr) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CNode* CConnman::FindNode(const CNetAddr& ip)
|
bool CConnman::AlreadyConnectedToHost(const std::string& host) const
|
||||||
{
|
{
|
||||||
LOCK(m_nodes_mutex);
|
LOCK(m_nodes_mutex);
|
||||||
for (CNode* pnode : m_nodes) {
|
return std::ranges::any_of(m_nodes, [&host](CNode* node) { return node->m_addr_name == host; });
|
||||||
if (static_cast<CNetAddr>(pnode->addr) == ip) {
|
|
||||||
return pnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CNode* CConnman::FindNode(const std::string& addrName)
|
bool CConnman::AlreadyConnectedToAddressPort(const CService& addr_port) const
|
||||||
{
|
{
|
||||||
LOCK(m_nodes_mutex);
|
LOCK(m_nodes_mutex);
|
||||||
for (CNode* pnode : m_nodes) {
|
return std::ranges::any_of(m_nodes, [&addr_port](CNode* node) { return node->addr == addr_port; });
|
||||||
if (pnode->m_addr_name == addrName) {
|
|
||||||
return pnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CNode* CConnman::FindNode(const CService& addr)
|
bool CConnman::AlreadyConnectedToAddress(const CNetAddr& addr) const
|
||||||
{
|
{
|
||||||
LOCK(m_nodes_mutex);
|
LOCK(m_nodes_mutex);
|
||||||
for (CNode* pnode : m_nodes) {
|
return std::ranges::any_of(m_nodes, [&addr](CNode* node) { return node->addr == addr; });
|
||||||
if (static_cast<CService>(pnode->addr) == addr) {
|
|
||||||
return pnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
|
|
||||||
{
|
|
||||||
return FindNode(static_cast<CNetAddr>(addr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CConnman::CheckIncomingNonce(uint64_t nonce)
|
bool CConnman::CheckIncomingNonce(uint64_t nonce)
|
||||||
|
@ -393,7 +373,12 @@ static CService GetBindAddress(const Sock& sock)
|
||||||
return addr_bind;
|
return addr_bind;
|
||||||
}
|
}
|
||||||
|
|
||||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport)
|
CNode* CConnman::ConnectNode(CAddress addrConnect,
|
||||||
|
const char* pszDest,
|
||||||
|
bool fCountFailure,
|
||||||
|
ConnectionType conn_type,
|
||||||
|
bool use_v2transport,
|
||||||
|
const std::optional<Proxy>& proxy_override)
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
assert(conn_type != ConnectionType::INBOUND);
|
assert(conn_type != ConnectionType::INBOUND);
|
||||||
|
@ -403,10 +388,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Look for an existing connection
|
// Look for an existing connection
|
||||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||||
if (pnode)
|
LogInfo("Failed to open new connection to %s, already connected", addrConnect.ToStringAddrPort());
|
||||||
{
|
|
||||||
LogPrintf("Failed to open new connection, already connected\n");
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,9 +419,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
}
|
}
|
||||||
// It is possible that we already have a connection to the IP/port pszDest resolved to.
|
// It is possible that we already have a connection to the IP/port pszDest resolved to.
|
||||||
// In that case, drop the connection that was just created.
|
// In that case, drop the connection that was just created.
|
||||||
LOCK(m_nodes_mutex);
|
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
|
||||||
if (pnode) {
|
|
||||||
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
|
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +444,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
|
|
||||||
for (auto& target_addr: connect_to) {
|
for (auto& target_addr: connect_to) {
|
||||||
if (target_addr.IsValid()) {
|
if (target_addr.IsValid()) {
|
||||||
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
bool use_proxy;
|
||||||
|
if (proxy_override.has_value()) {
|
||||||
|
use_proxy = true;
|
||||||
|
proxy = proxy_override.value();
|
||||||
|
} else {
|
||||||
|
use_proxy = GetProxy(target_addr.GetNetwork(), proxy);
|
||||||
|
}
|
||||||
bool proxyConnectionFailed = false;
|
bool proxyConnectionFailed = false;
|
||||||
|
|
||||||
if (target_addr.IsI2P() && use_proxy) {
|
if (target_addr.IsI2P() && use_proxy) {
|
||||||
|
@ -477,7 +464,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
LOCK(m_unused_i2p_sessions_mutex);
|
LOCK(m_unused_i2p_sessions_mutex);
|
||||||
if (m_unused_i2p_sessions.empty()) {
|
if (m_unused_i2p_sessions.empty()) {
|
||||||
i2p_transient_session =
|
i2p_transient_session =
|
||||||
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
|
std::make_unique<i2p::sam::Session>(proxy, m_interrupt_net);
|
||||||
} else {
|
} else {
|
||||||
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
||||||
m_unused_i2p_sessions.pop();
|
m_unused_i2p_sessions.pop();
|
||||||
|
@ -530,6 +517,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
if (!addr_bind.IsValid()) {
|
if (!addr_bind.IsValid()) {
|
||||||
addr_bind = GetBindAddress(*sock);
|
addr_bind = GetBindAddress(*sock);
|
||||||
}
|
}
|
||||||
|
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||||
|
.Write(target_addr.GetNetClass())
|
||||||
|
.Write(addr_bind.GetAddrBytes())
|
||||||
|
// For outbound connections, the port of the bound address is randomly
|
||||||
|
// assigned by the OS and would therefore not be useful for seeding.
|
||||||
|
.Write(0)
|
||||||
|
.Finalize();
|
||||||
CNode* pnode = new CNode(id,
|
CNode* pnode = new CNode(id,
|
||||||
std::move(sock),
|
std::move(sock),
|
||||||
target_addr,
|
target_addr,
|
||||||
|
@ -539,6 +533,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
pszDest ? pszDest : "",
|
pszDest ? pszDest : "",
|
||||||
conn_type,
|
conn_type,
|
||||||
/*inbound_onion=*/false,
|
/*inbound_onion=*/false,
|
||||||
|
network_id,
|
||||||
CNodeOptions{
|
CNodeOptions{
|
||||||
.permission_flags = permission_flags,
|
.permission_flags = permission_flags,
|
||||||
.i2p_sam_session = std::move(i2p_transient_session),
|
.i2p_sam_session = std::move(i2p_transient_session),
|
||||||
|
@ -574,9 +569,9 @@ void CNode::CloseSocketDisconnect()
|
||||||
m_i2p_sam_session.reset();
|
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) {
|
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);
|
NetPermissions::AddFlag(flags, subnet.m_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1638,7 +1633,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
|
||||||
flags |= MSG_MORE;
|
flags |= MSG_MORE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
nBytes = node.m_sock->Send(reinterpret_cast<const char*>(data.data()), data.size(), flags);
|
nBytes = node.m_sock->Send(data.data(), data.size(), flags);
|
||||||
}
|
}
|
||||||
if (nBytes > 0) {
|
if (nBytes > 0) {
|
||||||
node.m_last_send = GetTime<std::chrono::seconds>();
|
node.m_last_send = GetTime<std::chrono::seconds>();
|
||||||
|
@ -1768,7 +1763,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
||||||
{
|
{
|
||||||
int nInbound = 0;
|
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);
|
LOCK(m_nodes_mutex);
|
||||||
|
@ -1823,12 +1822,16 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
||||||
NodeId id = GetNewNodeId();
|
NodeId id = GetNewNodeId();
|
||||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
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
|
// The V2Transport transparently falls back to V1 behavior when an incoming V1 connection is
|
||||||
// detected, so use it whenever we signal NODE_P2P_V2.
|
// detected, so use it whenever we signal NODE_P2P_V2.
|
||||||
ServiceFlags local_services = GetLocalServices();
|
ServiceFlags local_services = GetLocalServices();
|
||||||
const bool use_v2transport(local_services & NODE_P2P_V2);
|
const bool use_v2transport(local_services & NODE_P2P_V2);
|
||||||
|
|
||||||
|
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||||
|
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
|
||||||
|
.Write(addr_bind.GetAddrBytes())
|
||||||
|
.Write(addr_bind.GetPort()) // inbound connections use bind port
|
||||||
|
.Finalize();
|
||||||
CNode* pnode = new CNode(id,
|
CNode* pnode = new CNode(id,
|
||||||
std::move(sock),
|
std::move(sock),
|
||||||
CAddress{addr, NODE_NONE},
|
CAddress{addr, NODE_NONE},
|
||||||
|
@ -1838,6 +1841,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::INBOUND,
|
ConnectionType::INBOUND,
|
||||||
inbound_onion,
|
inbound_onion,
|
||||||
|
network_id,
|
||||||
CNodeOptions{
|
CNodeOptions{
|
||||||
.permission_flags = permission_flags,
|
.permission_flags = permission_flags,
|
||||||
.prefer_evict = discouraged,
|
.prefer_evict = discouraged,
|
||||||
|
@ -2100,7 +2104,7 @@ void CConnman::SocketHandler()
|
||||||
// empty sets.
|
// empty sets.
|
||||||
events_per_sock = GenerateWaitSockets(snap.Nodes());
|
events_per_sock = GenerateWaitSockets(snap.Nodes());
|
||||||
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
|
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
|
||||||
interruptNet.sleep_for(timeout);
|
m_interrupt_net->sleep_for(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service (send/receive) each of the already connected nodes.
|
// Service (send/receive) each of the already connected nodes.
|
||||||
|
@ -2117,8 +2121,9 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
||||||
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
||||||
|
|
||||||
for (CNode* pnode : nodes) {
|
for (CNode* pnode : nodes) {
|
||||||
if (interruptNet)
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Receive
|
// Receive
|
||||||
|
@ -2213,7 +2218,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
||||||
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
|
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
|
||||||
{
|
{
|
||||||
for (const ListenSocket& listen_socket : vhListenSocket) {
|
for (const ListenSocket& listen_socket : vhListenSocket) {
|
||||||
if (interruptNet) {
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto it = events_per_sock.find(listen_socket.sock);
|
const auto it = events_per_sock.find(listen_socket.sock);
|
||||||
|
@ -2227,8 +2232,7 @@ void CConnman::ThreadSocketHandler()
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
DisconnectNodes();
|
DisconnectNodes();
|
||||||
NotifyNumConnectionsChanged();
|
NotifyNumConnectionsChanged();
|
||||||
SocketHandler();
|
SocketHandler();
|
||||||
|
@ -2252,9 +2256,10 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
auto start = NodeClock::now();
|
auto start = NodeClock::now();
|
||||||
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
|
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
|
||||||
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
|
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
|
||||||
while (!interruptNet) {
|
while (!m_interrupt_net->interrupted()) {
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Abort if we have spent enough time without reaching our target.
|
// Abort if we have spent enough time without reaching our target.
|
||||||
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
|
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
|
||||||
|
@ -2315,7 +2320,7 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
// early to see if we have enough peers and can stop
|
// early to see if we have enough peers and can stop
|
||||||
// this thread entirely freeing up its resources
|
// this thread entirely freeing up its resources
|
||||||
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
|
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
|
||||||
if (!interruptNet.sleep_for(w)) return;
|
if (!m_interrupt_net->sleep_for(w)) return;
|
||||||
to_wait -= w;
|
to_wait -= w;
|
||||||
|
|
||||||
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
|
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
|
||||||
|
@ -2331,13 +2336,13 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interruptNet) return;
|
if (m_interrupt_net->interrupted()) return;
|
||||||
|
|
||||||
// hold off on querying seeds if P2P network deactivated
|
// hold off on querying seeds if P2P network deactivated
|
||||||
if (!fNetworkActive) {
|
if (!fNetworkActive) {
|
||||||
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
|
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
|
||||||
do {
|
do {
|
||||||
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
|
if (!m_interrupt_net->sleep_for(1s)) return;
|
||||||
} while (!fNetworkActive);
|
} while (!fNetworkActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2532,12 +2537,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
|
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
|
||||||
for (int i = 0; i < 10 && i < nLoop; i++)
|
for (int i = 0; i < 10 && i < nLoop; i++)
|
||||||
{
|
{
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2561,8 +2568,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
LogPrintf("Fixed seeds are disabled\n");
|
LogPrintf("Fixed seeds are disabled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
if (add_addr_fetch) {
|
if (add_addr_fetch) {
|
||||||
add_addr_fetch = false;
|
add_addr_fetch = false;
|
||||||
const auto& seed{SpanPopBack(seed_nodes)};
|
const auto& seed{SpanPopBack(seed_nodes)};
|
||||||
|
@ -2577,14 +2583,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
|
|
||||||
ProcessAddrFetch();
|
ProcessAddrFetch();
|
||||||
|
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
|
|
||||||
CountingSemaphoreGrant<> grant(*semOutbound);
|
CountingSemaphoreGrant<> grant(*semOutbound);
|
||||||
if (interruptNet)
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
|
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
|
||||||
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
|
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
|
||||||
|
@ -2758,8 +2766,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
int nTries = 0;
|
int nTries = 0;
|
||||||
const auto reachable_nets{g_reachable_nets.All()};
|
const auto reachable_nets{g_reachable_nets.All()};
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
if (anchor && !m_anchors.empty()) {
|
if (anchor && !m_anchors.empty()) {
|
||||||
const CAddress addr = m_anchors.back();
|
const CAddress addr = m_anchors.back();
|
||||||
m_anchors.pop_back();
|
m_anchors.pop_back();
|
||||||
|
@ -2861,7 +2868,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
if (addrConnect.IsValid()) {
|
if (addrConnect.IsValid()) {
|
||||||
if (fFeeler) {
|
if (fFeeler) {
|
||||||
// Add small amount of random noise before connection to avoid synchronization.
|
// Add small amount of random noise before connection to avoid synchronization.
|
||||||
if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
|
if (!m_interrupt_net->sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
|
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
|
||||||
|
@ -2876,7 +2883,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
||||||
const bool count_failures{((int)outbound_ipv46_peer_netgroups.size() + outbound_privacy_network_peers) >= std::min(m_max_automatic_connections - 1, 2)};
|
const bool count_failures{((int)outbound_ipv46_peer_netgroups.size() + outbound_privacy_network_peers) >= std::min(m_max_automatic_connections - 1, 2)};
|
||||||
// Use BIP324 transport when both us and them have NODE_V2_P2P set.
|
// Use BIP324 transport when both us and them have NODE_V2_P2P set.
|
||||||
const bool use_v2transport(addrConnect.nServices & GetLocalServices() & NODE_P2P_V2);
|
const bool use_v2transport(addrConnect.nServices & GetLocalServices() & NODE_P2P_V2);
|
||||||
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*strDest=*/nullptr, conn_type, use_v2transport);
|
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*pszDest=*/nullptr, conn_type, use_v2transport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2972,19 +2979,26 @@ void CConnman::ThreadOpenAddedConnections()
|
||||||
tried = true;
|
tried = true;
|
||||||
CAddress addr(CService(), NODE_NONE);
|
CAddress addr(CService(), NODE_NONE);
|
||||||
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
|
if (!m_interrupt_net->sleep_for(500ms)) return;
|
||||||
grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
|
grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
|
||||||
}
|
}
|
||||||
// See if any reconnections are desired.
|
// See if any reconnections are desired.
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
|
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
|
||||||
if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))
|
if (!m_interrupt_net->sleep_for(tried ? 60s : 2s)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if successful, this moves the passed grant to the constructed node
|
// if successful, this moves the passed grant to the constructed node
|
||||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char *pszDest, ConnectionType conn_type, bool use_v2transport)
|
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect,
|
||||||
|
bool fCountFailure,
|
||||||
|
CountingSemaphoreGrant<>&& grant_outbound,
|
||||||
|
const char* pszDest,
|
||||||
|
ConnectionType conn_type,
|
||||||
|
bool use_v2transport,
|
||||||
|
const std::optional<Proxy>& proxy_override)
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
assert(conn_type != ConnectionType::INBOUND);
|
assert(conn_type != ConnectionType::INBOUND);
|
||||||
|
@ -2992,24 +3006,25 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||||
//
|
//
|
||||||
// Initiate outbound network connection
|
// Initiate outbound network connection
|
||||||
//
|
//
|
||||||
if (interruptNet) {
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (!fNetworkActive) {
|
if (!fNetworkActive) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (!pszDest) {
|
if (!pszDest) {
|
||||||
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
|
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
|
||||||
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
|
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (FindNode(std::string(pszDest)))
|
} else if (AlreadyConnectedToHost(pszDest)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport);
|
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport, proxy_override);
|
||||||
|
|
||||||
if (!pnode)
|
if (!pnode)
|
||||||
return;
|
return false;
|
||||||
pnode->grantOutbound = std::move(grant_outbound);
|
pnode->grantOutbound = std::move(grant_outbound);
|
||||||
|
|
||||||
m_msgproc->InitializeNode(*pnode, m_local_services);
|
m_msgproc->InitializeNode(*pnode, m_local_services);
|
||||||
|
@ -3027,6 +3042,8 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||||
pnode->ConnectionTypeAsString().c_str(),
|
pnode->ConnectionTypeAsString().c_str(),
|
||||||
pnode->ConnectedThroughNetwork(),
|
pnode->ConnectedThroughNetwork(),
|
||||||
GetNodeCount(ConnectionDirection::Out));
|
GetNodeCount(ConnectionDirection::Out));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex NetEventsInterface::g_msgproc_mutex;
|
Mutex NetEventsInterface::g_msgproc_mutex;
|
||||||
|
@ -3080,13 +3097,13 @@ void CConnman::ThreadI2PAcceptIncoming()
|
||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
|
|
||||||
auto SleepOnFailure = [&]() {
|
auto SleepOnFailure = [&]() {
|
||||||
interruptNet.sleep_for(err_wait);
|
m_interrupt_net->sleep_for(err_wait);
|
||||||
if (err_wait < err_wait_cap) {
|
if (err_wait < err_wait_cap) {
|
||||||
err_wait += 1s;
|
err_wait += 1s;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (!interruptNet) {
|
while (!m_interrupt_net->interrupted()) {
|
||||||
|
|
||||||
if (!m_i2p_sam_session->Listen(conn)) {
|
if (!m_i2p_sam_session->Listen(conn)) {
|
||||||
if (advertising_listen_addr && conn.me.IsValid()) {
|
if (advertising_listen_addr && conn.me.IsValid()) {
|
||||||
|
@ -3136,7 +3153,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
|
||||||
|
|
||||||
// Allow binding if the port is still in TIME_WAIT state after
|
// Allow binding if the port is still in TIME_WAIT state after
|
||||||
// the program was closed and restarted.
|
// the program was closed and restarted.
|
||||||
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
|
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &nOne, sizeof(int)) == SOCKET_ERROR) {
|
||||||
strError = Untranslated(strprintf("Error setting SO_REUSEADDR on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
strError = Untranslated(strprintf("Error setting SO_REUSEADDR on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
||||||
LogPrintf("%s\n", strError.original);
|
LogPrintf("%s\n", strError.original);
|
||||||
}
|
}
|
||||||
|
@ -3145,14 +3162,14 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
|
||||||
// and enable it by default or not. Try to enable it, if possible.
|
// and enable it by default or not. Try to enable it, if possible.
|
||||||
if (addrBind.IsIPv6()) {
|
if (addrBind.IsIPv6()) {
|
||||||
#ifdef IPV6_V6ONLY
|
#ifdef IPV6_V6ONLY
|
||||||
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
|
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, &nOne, sizeof(int)) == SOCKET_ERROR) {
|
||||||
strError = Untranslated(strprintf("Error setting IPV6_V6ONLY on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
strError = Untranslated(strprintf("Error setting IPV6_V6ONLY on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
||||||
LogPrintf("%s\n", strError.original);
|
LogPrintf("%s\n", strError.original);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
|
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
|
||||||
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
|
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &nProtLevel, sizeof(int)) == SOCKET_ERROR) {
|
||||||
strError = Untranslated(strprintf("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
strError = Untranslated(strprintf("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
|
||||||
LogPrintf("%s\n", strError.original);
|
LogPrintf("%s\n", strError.original);
|
||||||
}
|
}
|
||||||
|
@ -3208,12 +3225,18 @@ void CConnman::SetNetworkActive(bool active)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
|
CConnman::CConnman(uint64_t nSeed0In,
|
||||||
const NetGroupManager& netgroupman, const CChainParams& params, bool network_active)
|
uint64_t nSeed1In,
|
||||||
|
AddrMan& addrman_in,
|
||||||
|
const NetGroupManager& netgroupman,
|
||||||
|
const CChainParams& params,
|
||||||
|
bool network_active,
|
||||||
|
std::shared_ptr<CThreadInterrupt> interrupt_net)
|
||||||
: addrman(addrman_in)
|
: addrman(addrman_in)
|
||||||
, m_netgroupman{netgroupman}
|
, m_netgroupman{netgroupman}
|
||||||
, nSeed0(nSeed0In)
|
, nSeed0(nSeed0In)
|
||||||
, nSeed1(nSeed1In)
|
, nSeed1(nSeed1In)
|
||||||
|
, m_interrupt_net{interrupt_net}
|
||||||
, m_params(params)
|
, m_params(params)
|
||||||
{
|
{
|
||||||
SetTryNewOutboundPeer(false);
|
SetTryNewOutboundPeer(false);
|
||||||
|
@ -3309,7 +3332,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||||
Proxy i2p_sam;
|
Proxy i2p_sam;
|
||||||
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
|
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
|
||||||
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
|
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
|
||||||
i2p_sam, &interruptNet);
|
i2p_sam, m_interrupt_net);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Randomize the order in which we may query seednode to potentially prevent connecting to the same one every restart (and signal that we have restarted)
|
// Randomize the order in which we may query seednode to potentially prevent connecting to the same one every restart (and signal that we have restarted)
|
||||||
|
@ -3346,7 +3369,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||||
// Start threads
|
// Start threads
|
||||||
//
|
//
|
||||||
assert(m_msgproc);
|
assert(m_msgproc);
|
||||||
interruptNet.reset();
|
m_interrupt_net->reset();
|
||||||
flagInterruptMsgProc = false;
|
flagInterruptMsgProc = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -3422,7 +3445,7 @@ void CConnman::Interrupt()
|
||||||
}
|
}
|
||||||
condMsgProc.notify_all();
|
condMsgProc.notify_all();
|
||||||
|
|
||||||
interruptNet();
|
(*m_interrupt_net)();
|
||||||
g_socks5_interrupt();
|
g_socks5_interrupt();
|
||||||
|
|
||||||
if (semOutbound) {
|
if (semOutbound) {
|
||||||
|
@ -3516,15 +3539,9 @@ std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t
|
||||||
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
||||||
{
|
{
|
||||||
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
||||||
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
uint64_t network_id = requestor.m_network_key;
|
||||||
.Write(requestor.ConnectedThroughNetwork())
|
|
||||||
.Write(local_socket_bytes)
|
|
||||||
// For outbound connections, the port of the bound address is randomly
|
|
||||||
// assigned by the OS and would therefore not be useful for seeding.
|
|
||||||
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
|
|
||||||
.Finalize();
|
|
||||||
const auto current_time = GetTime<std::chrono::microseconds>();
|
const auto current_time = GetTime<std::chrono::microseconds>();
|
||||||
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
|
||||||
CachedAddrResponse& cache_entry = r.first->second;
|
CachedAddrResponse& cache_entry = r.first->second;
|
||||||
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
||||||
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
||||||
|
@ -3638,9 +3655,11 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
|
||||||
bool CConnman::DisconnectNode(const std::string& strNode)
|
bool CConnman::DisconnectNode(const std::string& strNode)
|
||||||
{
|
{
|
||||||
LOCK(m_nodes_mutex);
|
LOCK(m_nodes_mutex);
|
||||||
if (CNode* pnode = FindNode(strNode)) {
|
auto it = std::ranges::find_if(m_nodes, [&strNode](CNode* node) { return node->m_addr_name == strNode; });
|
||||||
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->DisconnectMsg(fLogIPs));
|
if (it != m_nodes.end()) {
|
||||||
pnode->fDisconnect = true;
|
CNode* node{*it};
|
||||||
|
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), node->DisconnectMsg(fLogIPs));
|
||||||
|
node->fDisconnect = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -3801,6 +3820,7 @@ CNode::CNode(NodeId idIn,
|
||||||
const std::string& addrNameIn,
|
const std::string& addrNameIn,
|
||||||
ConnectionType conn_type_in,
|
ConnectionType conn_type_in,
|
||||||
bool inbound_onion,
|
bool inbound_onion,
|
||||||
|
uint64_t network_key,
|
||||||
CNodeOptions&& node_opts)
|
CNodeOptions&& node_opts)
|
||||||
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
||||||
m_permission_flags{node_opts.permission_flags},
|
m_permission_flags{node_opts.permission_flags},
|
||||||
|
@ -3813,6 +3833,7 @@ CNode::CNode(NodeId idIn,
|
||||||
m_inbound_onion{inbound_onion},
|
m_inbound_onion{inbound_onion},
|
||||||
m_prefer_evict{node_opts.prefer_evict},
|
m_prefer_evict{node_opts.prefer_evict},
|
||||||
nKeyedNetGroup{nKeyedNetGroupIn},
|
nKeyedNetGroup{nKeyedNetGroupIn},
|
||||||
|
m_network_key{network_key},
|
||||||
m_conn_type{conn_type_in},
|
m_conn_type{conn_type_in},
|
||||||
id{idIn},
|
id{idIn},
|
||||||
nLocalHostNonce{nLocalHostNonceIn},
|
nLocalHostNonce{nLocalHostNonceIn},
|
||||||
|
|
90
src/net.h
90
src/net.h
|
@ -738,6 +738,10 @@ public:
|
||||||
std::atomic_bool fPauseRecv{false};
|
std::atomic_bool fPauseRecv{false};
|
||||||
std::atomic_bool fPauseSend{false};
|
std::atomic_bool fPauseSend{false};
|
||||||
|
|
||||||
|
/** Network key used to prevent fingerprinting our node across networks.
|
||||||
|
* Influenced by the network and the bind address (+ bind port for inbounds) */
|
||||||
|
const uint64_t m_network_key;
|
||||||
|
|
||||||
const ConnectionType m_conn_type;
|
const ConnectionType m_conn_type;
|
||||||
|
|
||||||
/** Move all messages from the received queue to the processing queue. */
|
/** Move all messages from the received queue to the processing queue. */
|
||||||
|
@ -889,6 +893,7 @@ public:
|
||||||
const std::string& addrNameIn,
|
const std::string& addrNameIn,
|
||||||
ConnectionType conn_type_in,
|
ConnectionType conn_type_in,
|
||||||
bool inbound_onion,
|
bool inbound_onion,
|
||||||
|
uint64_t network_key,
|
||||||
CNodeOptions&& node_opts = {});
|
CNodeOptions&& node_opts = {});
|
||||||
CNode(const CNode&) = delete;
|
CNode(const CNode&) = delete;
|
||||||
CNode& operator=(const CNode&) = delete;
|
CNode& operator=(const CNode&) = delete;
|
||||||
|
@ -1119,8 +1124,13 @@ public:
|
||||||
whitelist_relay = connOptions.whitelist_relay;
|
whitelist_relay = connOptions.whitelist_relay;
|
||||||
}
|
}
|
||||||
|
|
||||||
CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman,
|
CConnman(uint64_t seed0,
|
||||||
const CChainParams& params, bool network_active = true);
|
uint64_t seed1,
|
||||||
|
AddrMan& addrman,
|
||||||
|
const NetGroupManager& netgroupman,
|
||||||
|
const CChainParams& params,
|
||||||
|
bool network_active = true,
|
||||||
|
std::shared_ptr<CThreadInterrupt> interrupt_net = std::make_shared<CThreadInterrupt>());
|
||||||
|
|
||||||
~CConnman();
|
~CConnman();
|
||||||
|
|
||||||
|
@ -1138,7 +1148,28 @@ public:
|
||||||
bool GetNetworkActive() const { return fNetworkActive; };
|
bool GetNetworkActive() const { return fNetworkActive; };
|
||||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||||
void SetNetworkActive(bool active);
|
void SetNetworkActive(bool active);
|
||||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char* strDest, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
|
||||||
|
/**
|
||||||
|
* Open a new P2P connection and initialize it with the PeerManager at `m_msgproc`.
|
||||||
|
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||||
|
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||||
|
* @param[in] grant_outbound Take ownership of this grant, to be released later when the connection is closed.
|
||||||
|
* @param[in] pszDest Address to resolve and connect to.
|
||||||
|
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||||
|
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||||
|
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||||
|
* @retval true The connection was opened successfully.
|
||||||
|
* @retval false The connection attempt failed.
|
||||||
|
*/
|
||||||
|
bool OpenNetworkConnection(const CAddress& addrConnect,
|
||||||
|
bool fCountFailure,
|
||||||
|
CountingSemaphoreGrant<>&& grant_outbound,
|
||||||
|
const char* pszDest,
|
||||||
|
ConnectionType conn_type,
|
||||||
|
bool use_v2transport,
|
||||||
|
const std::optional<Proxy>& proxy_override = std::nullopt)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||||
|
|
||||||
bool CheckIncomingNonce(uint64_t nonce);
|
bool CheckIncomingNonce(uint64_t nonce);
|
||||||
void ASMapHealthCheck();
|
void ASMapHealthCheck();
|
||||||
|
|
||||||
|
@ -1365,19 +1396,50 @@ private:
|
||||||
|
|
||||||
uint64_t CalculateKeyedNetGroup(const CNetAddr& ad) const;
|
uint64_t CalculateKeyedNetGroup(const CNetAddr& ad) const;
|
||||||
|
|
||||||
CNode* FindNode(const CNetAddr& ip);
|
/**
|
||||||
CNode* FindNode(const std::string& addrName);
|
* Determine whether we're already connected to a given "host:port".
|
||||||
CNode* FindNode(const CService& addr);
|
* Note that for inbound connections, the peer is likely using a random outbound
|
||||||
|
* port on their side, so this will likely not match any inbound connections.
|
||||||
|
* @param[in] host String of the form "host[:port]", e.g. "localhost" or "localhost:8333" or "1.2.3.4:8333".
|
||||||
|
* @return true if connected to `host`.
|
||||||
|
*/
|
||||||
|
bool AlreadyConnectedToHost(const std::string& host) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether we're already connected to a given address, in order to
|
* Determine whether we're already connected to a given address:port.
|
||||||
* avoid initiating duplicate connections.
|
* Note that for inbound connections, the peer is likely using a random outbound
|
||||||
|
* port on their side, so this will likely not match any inbound connections.
|
||||||
|
* @param[in] addr_port Address and port to check.
|
||||||
|
* @return true if connected to addr_port.
|
||||||
*/
|
*/
|
||||||
bool AlreadyConnectedToAddress(const CAddress& addr);
|
bool AlreadyConnectedToAddressPort(const CService& addr_port) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether we're already connected to a given address.
|
||||||
|
*/
|
||||||
|
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
|
||||||
|
|
||||||
bool AttemptToEvictConnection();
|
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;
|
/**
|
||||||
|
* Open a new P2P connection.
|
||||||
|
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||||
|
* @param[in] pszDest Address to resolve and connect to.
|
||||||
|
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||||
|
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||||
|
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||||
|
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||||
|
* @return Newly created CNode object or nullptr if the connection failed.
|
||||||
|
*/
|
||||||
|
CNode* ConnectNode(CAddress addrConnect,
|
||||||
|
const char* pszDest,
|
||||||
|
bool fCountFailure,
|
||||||
|
ConnectionType conn_type,
|
||||||
|
bool use_v2transport,
|
||||||
|
const std::optional<Proxy>& proxy_override)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||||
|
|
||||||
|
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||||
|
|
||||||
void DeleteNode(CNode* pnode);
|
void DeleteNode(CNode* pnode);
|
||||||
|
|
||||||
|
@ -1555,11 +1617,9 @@ private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is signaled when network activity should cease.
|
* This is signaled when network activity should cease.
|
||||||
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
* A copy of this is saved in `m_i2p_sam_session`.
|
||||||
* the lifetime of `interruptNet` is not shorter than
|
|
||||||
* the lifetime of `m_i2p_sam_session`.
|
|
||||||
*/
|
*/
|
||||||
CThreadInterrupt interruptNet;
|
const std::shared_ptr<CThreadInterrupt> m_interrupt_net;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* I2P SAM session.
|
* I2P SAM session.
|
||||||
|
|
|
@ -167,7 +167,7 @@ static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s};
|
||||||
static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
|
static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
|
||||||
/** Maximum rate of inventory items to send per second.
|
/** Maximum rate of inventory items to send per second.
|
||||||
* Limits the impact of low-fee transaction floods. */
|
* 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. */
|
/** 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);
|
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. */
|
/** Maximum number of inventory items to send per transmission. */
|
||||||
|
@ -312,7 +312,7 @@ struct Peer {
|
||||||
std::chrono::microseconds m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){0};
|
std::chrono::microseconds m_next_inv_send_time GUARDED_BY(m_tx_inventory_mutex){0};
|
||||||
/** The mempool sequence num at which we sent the last `inv` message to this peer.
|
/** The mempool sequence num at which we sent the last `inv` message to this peer.
|
||||||
* Can relay txs with lower sequence numbers than this (see CTxMempool::info_for_relay). */
|
* Can relay txs with lower sequence numbers than this (see CTxMempool::info_for_relay). */
|
||||||
uint64_t m_last_inv_sequence GUARDED_BY(NetEventsInterface::g_msgproc_mutex){1};
|
uint64_t m_last_inv_sequence GUARDED_BY(m_tx_inventory_mutex){1};
|
||||||
|
|
||||||
/** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
|
/** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
|
||||||
std::atomic<CAmount> m_fee_filter_received{0};
|
std::atomic<CAmount> m_fee_filter_received{0};
|
||||||
|
@ -807,7 +807,7 @@ private:
|
||||||
|
|
||||||
uint32_t GetFetchFlags(const Peer& peer) const;
|
uint32_t GetFetchFlags(const Peer& peer) const;
|
||||||
|
|
||||||
std::atomic<std::chrono::microseconds> m_next_inv_to_inbounds{0us};
|
std::map<uint64_t, std::chrono::microseconds> m_next_inv_to_inbounds_per_network_key GUARDED_BY(g_msgproc_mutex);
|
||||||
|
|
||||||
/** Number of nodes with fSyncStarted. */
|
/** Number of nodes with fSyncStarted. */
|
||||||
int nSyncStarted GUARDED_BY(cs_main) = 0;
|
int nSyncStarted GUARDED_BY(cs_main) = 0;
|
||||||
|
@ -837,12 +837,14 @@ private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For sending `inv`s to inbound peers, we use a single (exponentially
|
* For sending `inv`s to inbound peers, we use a single (exponentially
|
||||||
* distributed) timer for all peers. If we used a separate timer for each
|
* distributed) timer for all peers with the same network key. If we used a separate timer for each
|
||||||
* peer, a spy node could make multiple inbound connections to us to
|
* peer, a spy node could make multiple inbound connections to us to
|
||||||
* accurately determine when we received the transaction (and potentially
|
* accurately determine when we received a transaction (and potentially
|
||||||
* determine the transaction's origin). */
|
* determine the transaction's origin). Each network key has its own timer
|
||||||
|
* to make fingerprinting harder. */
|
||||||
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
|
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
|
||||||
std::chrono::seconds average_interval) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
std::chrono::seconds average_interval,
|
||||||
|
uint64_t network_key) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||||
|
|
||||||
|
|
||||||
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
|
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
|
||||||
|
@ -942,7 +944,7 @@ private:
|
||||||
|
|
||||||
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
|
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
|
||||||
CTransactionRef FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid)
|
CTransactionRef FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, NetEventsInterface::g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, !tx_relay.m_tx_inventory_mutex);
|
||||||
|
|
||||||
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
|
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, peer.m_getdata_requests_mutex, NetEventsInterface::g_msgproc_mutex)
|
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, peer.m_getdata_requests_mutex, NetEventsInterface::g_msgproc_mutex)
|
||||||
|
@ -1143,15 +1145,15 @@ static bool CanServeWitnesses(const Peer& peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
|
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
|
||||||
std::chrono::seconds average_interval)
|
std::chrono::seconds average_interval,
|
||||||
|
uint64_t network_key)
|
||||||
{
|
{
|
||||||
if (m_next_inv_to_inbounds.load() < now) {
|
auto [it, inserted] = m_next_inv_to_inbounds_per_network_key.try_emplace(network_key, 0us);
|
||||||
// If this function were called from multiple threads simultaneously
|
auto& timer{it->second};
|
||||||
// it would possible that both update the next send variable, and return a different result to their caller.
|
if (timer < now) {
|
||||||
// This is not possible in practice as only the net processing thread invokes this function.
|
timer = now + m_rng.rand_exp_duration(average_interval);
|
||||||
m_next_inv_to_inbounds = now + m_rng.rand_exp_duration(average_interval);
|
|
||||||
}
|
}
|
||||||
return m_next_inv_to_inbounds;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
|
bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
|
||||||
|
@ -1728,9 +1730,13 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
|
||||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||||
stats.m_relay_txs = WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs);
|
stats.m_relay_txs = WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs);
|
||||||
stats.m_fee_filter_received = tx_relay->m_fee_filter_received.load();
|
stats.m_fee_filter_received = tx_relay->m_fee_filter_received.load();
|
||||||
|
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||||
|
stats.m_last_inv_seq = tx_relay->m_last_inv_sequence;
|
||||||
|
stats.m_inv_to_send = tx_relay->m_tx_inventory_to_send.size();
|
||||||
} else {
|
} else {
|
||||||
stats.m_relay_txs = false;
|
stats.m_relay_txs = false;
|
||||||
stats.m_fee_filter_received = 0;
|
stats.m_fee_filter_received = 0;
|
||||||
|
stats.m_inv_to_send = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.m_ping_wait = ping_wait;
|
stats.m_ping_wait = ping_wait;
|
||||||
|
@ -2362,8 +2368,8 @@ CTransactionRef PeerManagerImpl::FindTxForGetData(const Peer::TxRelay& tx_relay,
|
||||||
{
|
{
|
||||||
// If a tx was in the mempool prior to the last INV for this peer, permit the request.
|
// If a tx was in the mempool prior to the last INV for this peer, permit the request.
|
||||||
auto txinfo{std::visit(
|
auto txinfo{std::visit(
|
||||||
[&](const auto& id) EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex) {
|
[&](const auto& id) {
|
||||||
return m_mempool.info_for_relay(id, tx_relay.m_last_inv_sequence);
|
return m_mempool.info_for_relay(id, WITH_LOCK(tx_relay.m_tx_inventory_mutex, return tx_relay.m_last_inv_sequence));
|
||||||
},
|
},
|
||||||
gtxid)};
|
gtxid)};
|
||||||
if (txinfo.tx) {
|
if (txinfo.tx) {
|
||||||
|
@ -5711,7 +5717,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
if (tx_relay->m_next_inv_send_time < current_time) {
|
if (tx_relay->m_next_inv_send_time < current_time) {
|
||||||
fSendTrickle = true;
|
fSendTrickle = true;
|
||||||
if (pto->IsInboundConn()) {
|
if (pto->IsInboundConn()) {
|
||||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL, pto->m_network_key);
|
||||||
} else {
|
} else {
|
||||||
tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ struct CNodeStateStats {
|
||||||
std::chrono::microseconds m_ping_wait;
|
std::chrono::microseconds m_ping_wait;
|
||||||
std::vector<int> vHeightInFlight;
|
std::vector<int> vHeightInFlight;
|
||||||
bool m_relay_txs;
|
bool m_relay_txs;
|
||||||
|
int m_inv_to_send = 0;
|
||||||
|
uint64_t m_last_inv_seq{0};
|
||||||
CAmount m_fee_filter_received;
|
CAmount m_fee_filter_received;
|
||||||
uint64_t m_addr_processed = 0;
|
uint64_t m_addr_processed = 0;
|
||||||
uint64_t m_addr_rate_limited = 0;
|
uint64_t m_addr_rate_limited = 0;
|
||||||
|
|
|
@ -551,7 +551,7 @@ std::unique_ptr<Sock> CreateSockOS(int domain, int type, int protocol)
|
||||||
int set = 1;
|
int set = 1;
|
||||||
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
|
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
|
||||||
// should use the MSG_NOSIGNAL flag for every send.
|
// should use the MSG_NOSIGNAL flag for every send.
|
||||||
if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)) == SOCKET_ERROR) {
|
if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(int)) == SOCKET_ERROR) {
|
||||||
LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n",
|
LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n",
|
||||||
NetworkErrorString(WSAGetLastError()));
|
NetworkErrorString(WSAGetLastError()));
|
||||||
}
|
}
|
||||||
|
@ -620,7 +620,7 @@ static bool ConnectToSocket(const Sock& sock, struct sockaddr* sockaddr, socklen
|
||||||
// sockerr here.
|
// sockerr here.
|
||||||
int sockerr;
|
int sockerr;
|
||||||
socklen_t sockerr_len = sizeof(sockerr);
|
socklen_t sockerr_len = sizeof(sockerr);
|
||||||
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) ==
|
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, &sockerr, &sockerr_len) ==
|
||||||
SOCKET_ERROR) {
|
SOCKET_ERROR) {
|
||||||
LogPrintf("getsockopt() for %s failed: %s\n", dest_str, NetworkErrorString(WSAGetLastError()));
|
LogPrintf("getsockopt() for %s failed: %s\n", dest_str, NetworkErrorString(WSAGetLastError()));
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
#include <node/caches.h>
|
#include <node/caches.h>
|
||||||
|
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
|
#include <common/system.h>
|
||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
#include <kernel/caches.h>
|
#include <kernel/caches.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
#include <node/interface_ui.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
#include <util/byte_units.h>
|
#include <util/byte_units.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#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};
|
static constexpr size_t MAX_32BIT_DBCACHE{1024_MiB};
|
||||||
|
|
||||||
namespace node {
|
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)
|
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{CalculateDbCacheBytes(args)};
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexCacheSizes index_sizes;
|
IndexCacheSizes index_sizes;
|
||||||
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
|
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}};
|
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
|
} // namespace node
|
||||||
|
|
|
@ -27,6 +27,13 @@ struct CacheSizes {
|
||||||
kernel::CacheSizes kernel;
|
kernel::CacheSizes kernel;
|
||||||
};
|
};
|
||||||
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
|
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
|
} // namespace node
|
||||||
|
|
||||||
#endif // BITCOIN_NODE_CACHES_H
|
#endif // BITCOIN_NODE_CACHES_H
|
||||||
|
|
|
@ -397,8 +397,8 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
||||||
|
|
||||||
++nConsecutiveFailed;
|
++nConsecutiveFailed;
|
||||||
|
|
||||||
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
|
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight +
|
||||||
m_options.nBlockMaxWeight - BLOCK_FULL_ENOUGH_WEIGHT_DELTA) {
|
BLOCK_FULL_ENOUGH_WEIGHT_DELTA > m_options.nBlockMaxWeight) {
|
||||||
// Give up if we're close to full and haven't succeeded in a while
|
// Give up if we're close to full and haven't succeeded in a while
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ static constexpr unsigned int MAX_DUST_OUTPUTS_PER_TX{1};
|
||||||
* Note that this does not affect consensus validity; see GetBlockScriptFlags()
|
* Note that this does not affect consensus validity; see GetBlockScriptFlags()
|
||||||
* for that.
|
* for that.
|
||||||
*/
|
*/
|
||||||
static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
static constexpr script_verify_flags MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||||
SCRIPT_VERIFY_DERSIG |
|
SCRIPT_VERIFY_DERSIG |
|
||||||
SCRIPT_VERIFY_NULLDUMMY |
|
SCRIPT_VERIFY_NULLDUMMY |
|
||||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
||||||
|
@ -112,7 +112,7 @@ static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||||
* the additional (non-mandatory) rules here, to improve forwards and
|
* the additional (non-mandatory) rules here, to improve forwards and
|
||||||
* backwards compatibility.
|
* backwards compatibility.
|
||||||
*/
|
*/
|
||||||
static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
static constexpr script_verify_flags STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
||||||
SCRIPT_VERIFY_STRICTENC |
|
SCRIPT_VERIFY_STRICTENC |
|
||||||
SCRIPT_VERIFY_MINIMALDATA |
|
SCRIPT_VERIFY_MINIMALDATA |
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
|
||||||
|
@ -128,7 +128,7 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
|
||||||
|
|
||||||
/** For convenience, standard but not mandatory verify flags. */
|
/** For convenience, standard but not mandatory verify flags. */
|
||||||
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
|
static constexpr script_verify_flags STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
|
||||||
|
|
||||||
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
|
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
|
||||||
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
|
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
|
||||||
|
|
|
@ -166,7 +166,7 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
|
||||||
result.pushKV("mediantime", blockindex.GetMedianTimePast());
|
result.pushKV("mediantime", blockindex.GetMedianTimePast());
|
||||||
result.pushKV("nonce", blockindex.nNonce);
|
result.pushKV("nonce", blockindex.nNonce);
|
||||||
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
|
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("difficulty", GetDifficulty(blockindex));
|
||||||
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
|
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
|
||||||
result.pushKV("nTx", blockindex.nTx);
|
result.pushKV("nTx", blockindex.nTx);
|
||||||
|
@ -1469,6 +1469,9 @@ RPCHelpMan getdeploymentinfo()
|
||||||
RPCResult::Type::OBJ, "", "", {
|
RPCResult::Type::OBJ, "", "", {
|
||||||
{RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
|
{RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
|
||||||
{RPCResult::Type::NUM, "height", "requested block height (or tip)"},
|
{RPCResult::Type::NUM, "height", "requested block height (or tip)"},
|
||||||
|
{RPCResult::Type::ARR, "script_flags", "script verify flags for the block", {
|
||||||
|
{RPCResult::Type::STR, "flag", "a script verify flag"},
|
||||||
|
}},
|
||||||
{RPCResult::Type::OBJ_DYN, "deployments", "", {
|
{RPCResult::Type::OBJ_DYN, "deployments", "", {
|
||||||
{RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
|
{RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
|
||||||
}},
|
}},
|
||||||
|
@ -1495,6 +1498,12 @@ RPCHelpMan getdeploymentinfo()
|
||||||
UniValue deploymentinfo(UniValue::VOBJ);
|
UniValue deploymentinfo(UniValue::VOBJ);
|
||||||
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
|
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
|
||||||
deploymentinfo.pushKV("height", blockindex->nHeight);
|
deploymentinfo.pushKV("height", blockindex->nHeight);
|
||||||
|
{
|
||||||
|
const auto flagnames = GetScriptFlagNames(GetBlockScriptFlags(*blockindex, chainman));
|
||||||
|
UniValue uv_flagnames(UniValue::VARR);
|
||||||
|
uv_flagnames.push_backV(flagnames.begin(), flagnames.end());
|
||||||
|
deploymentinfo.pushKV("script_flags", uv_flagnames);
|
||||||
|
}
|
||||||
deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
|
deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
|
||||||
return deploymentinfo;
|
return deploymentinfo;
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,6 +18,7 @@ public:
|
||||||
std::string methodName; //!< method whose params want conversion
|
std::string methodName; //!< method whose params want conversion
|
||||||
int paramIdx; //!< 0-based idx of param to convert
|
int paramIdx; //!< 0-based idx of param to convert
|
||||||
std::string paramName; //!< parameter name
|
std::string paramName; //!< parameter name
|
||||||
|
bool also_string{false}; //!< The parameter is also a string
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -188,10 +189,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "gettxout", 1, "n" },
|
{ "gettxout", 1, "n" },
|
||||||
{ "gettxout", 2, "include_mempool" },
|
{ "gettxout", 2, "include_mempool" },
|
||||||
{ "gettxoutproof", 0, "txids" },
|
{ "gettxoutproof", 0, "txids" },
|
||||||
{ "gettxoutsetinfo", 1, "hash_or_height" },
|
{ "gettxoutsetinfo", 1, "hash_or_height", /*also_string=*/true },
|
||||||
{ "gettxoutsetinfo", 2, "use_index"},
|
{ "gettxoutsetinfo", 2, "use_index"},
|
||||||
{ "dumptxoutset", 2, "options" },
|
{ "dumptxoutset", 2, "options" },
|
||||||
{ "dumptxoutset", 2, "rollback" },
|
{ "dumptxoutset", 2, "rollback", /*also_string=*/true },
|
||||||
{ "lockunspent", 0, "unlock" },
|
{ "lockunspent", 0, "unlock" },
|
||||||
{ "lockunspent", 1, "transactions" },
|
{ "lockunspent", 1, "transactions" },
|
||||||
{ "lockunspent", 2, "persistent" },
|
{ "lockunspent", 2, "persistent" },
|
||||||
|
@ -246,7 +247,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "listdescriptors", 0, "private" },
|
{ "listdescriptors", 0, "private" },
|
||||||
{ "verifychain", 0, "checklevel" },
|
{ "verifychain", 0, "checklevel" },
|
||||||
{ "verifychain", 1, "nblocks" },
|
{ "verifychain", 1, "nblocks" },
|
||||||
{ "getblockstats", 0, "hash_or_height" },
|
{ "getblockstats", 0, "hash_or_height", /*also_string=*/true },
|
||||||
{ "getblockstats", 1, "stats" },
|
{ "getblockstats", 1, "stats" },
|
||||||
{ "pruneblockchain", 0, "height" },
|
{ "pruneblockchain", 0, "height" },
|
||||||
{ "keypoolrefill", 0, "newsize" },
|
{ "keypoolrefill", 0, "newsize" },
|
||||||
|
@ -318,18 +319,21 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
/** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
|
/** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
|
||||||
static UniValue Parse(std::string_view raw)
|
static UniValue Parse(std::string_view raw, bool also_string)
|
||||||
{
|
{
|
||||||
UniValue parsed;
|
UniValue parsed;
|
||||||
if (!parsed.read(raw)) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
|
if (!parsed.read(raw)) {
|
||||||
|
if (!also_string) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CRPCConvertTable
|
class CRPCConvertTable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::set<std::pair<std::string, int>> members;
|
std::map<std::pair<std::string, int>, bool> members;
|
||||||
std::set<std::pair<std::string, std::string>> membersByName;
|
std::map<std::pair<std::string, std::string>, bool> membersByName;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CRPCConvertTable();
|
CRPCConvertTable();
|
||||||
|
@ -337,21 +341,29 @@ public:
|
||||||
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
|
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
|
||||||
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
|
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
|
||||||
{
|
{
|
||||||
return members.count({method, param_idx}) > 0 ? Parse(arg_value) : arg_value;
|
const auto& it = members.find({method, param_idx});
|
||||||
|
if (it != members.end()) {
|
||||||
|
return Parse(arg_value, it->second);
|
||||||
|
}
|
||||||
|
return arg_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
|
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
|
||||||
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
|
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
|
||||||
{
|
{
|
||||||
return membersByName.count({method, param_name}) > 0 ? Parse(arg_value) : arg_value;
|
const auto& it = membersByName.find({method, param_name});
|
||||||
|
if (it != membersByName.end()) {
|
||||||
|
return Parse(arg_value, it->second);
|
||||||
|
}
|
||||||
|
return arg_value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CRPCConvertTable::CRPCConvertTable()
|
CRPCConvertTable::CRPCConvertTable()
|
||||||
{
|
{
|
||||||
for (const auto& cp : vRPCConvertParams) {
|
for (const auto& cp : vRPCConvertParams) {
|
||||||
members.emplace(cp.methodName, cp.paramIdx);
|
members.emplace(std::make_pair(cp.methodName, cp.paramIdx), cp.also_string);
|
||||||
membersByName.emplace(cp.methodName, cp.paramName);
|
membersByName.emplace(std::make_pair(cp.methodName, cp.paramName), cp.also_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -955,7 +955,7 @@ static RPCHelpMan submitpackage()
|
||||||
RPCResult::Type::OBJ, "", "",
|
RPCResult::Type::OBJ, "", "",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
|
{RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
|
||||||
{RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by wtxid",
|
{RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
|
{RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
|
||||||
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
|
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
|
||||||
|
@ -968,7 +968,7 @@ static RPCHelpMan submitpackage()
|
||||||
{{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
|
{{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
{RPCResult::Type::STR, "error", /*optional=*/true, "The transaction error string, if it was rejected by the mempool"},
|
{RPCResult::Type::STR, "error", /*optional=*/true, "Error string if rejected from mempool, or \"package-not-validated\" when the package aborts before any per-tx processing."},
|
||||||
}}
|
}}
|
||||||
}},
|
}},
|
||||||
{RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
|
{RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
|
||||||
|
@ -1082,10 +1082,15 @@ static RPCHelpMan submitpackage()
|
||||||
for (const auto& tx : txns) {
|
for (const auto& tx : txns) {
|
||||||
UniValue result_inner{UniValue::VOBJ};
|
UniValue result_inner{UniValue::VOBJ};
|
||||||
result_inner.pushKV("txid", tx->GetHash().GetHex());
|
result_inner.pushKV("txid", tx->GetHash().GetHex());
|
||||||
|
const auto wtxid_hex = tx->GetWitnessHash().GetHex();
|
||||||
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
|
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
|
||||||
if (it == package_result.m_tx_results.end()) {
|
if (it == package_result.m_tx_results.end()) {
|
||||||
// No results, report error and continue
|
// No per-tx result for this wtxid
|
||||||
result_inner.pushKV("error", "unevaluated");
|
// Current invariant: per-tx results are all-or-none (every member or empty on package abort).
|
||||||
|
// If any exist yet this one is missing, it's an unexpected partial map.
|
||||||
|
CHECK_NONFATAL(package_result.m_tx_results.empty());
|
||||||
|
result_inner.pushKV("error", "package-not-validated");
|
||||||
|
tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& tx_result = it->second;
|
const auto& tx_result = it->second;
|
||||||
|
@ -1118,7 +1123,7 @@ static RPCHelpMan submitpackage()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), std::move(result_inner));
|
tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
|
||||||
}
|
}
|
||||||
rpc_result.pushKV("tx-results", std::move(tx_result_map));
|
rpc_result.pushKV("tx-results", std::move(tx_result_map));
|
||||||
UniValue replaced_list(UniValue::VARR);
|
UniValue replaced_list(UniValue::VARR);
|
||||||
|
|
|
@ -130,7 +130,7 @@ static RPCHelpMan getpeerinfo()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
{RPCResult::Type::NUM, "id", "Peer index"},
|
{RPCResult::Type::NUM, "id", "Peer index"},
|
||||||
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
|
{RPCResult::Type::STR, "addr", "(host:port) The IP address/hostname optionally followed by :port of the peer"},
|
||||||
{RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
|
{RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
|
||||||
{RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
|
{RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
|
||||||
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
|
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
|
||||||
|
@ -142,6 +142,8 @@ static RPCHelpMan getpeerinfo()
|
||||||
{RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
|
{RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
|
||||||
}},
|
}},
|
||||||
{RPCResult::Type::BOOL, "relaytxes", "Whether we relay transactions to this peer"},
|
{RPCResult::Type::BOOL, "relaytxes", "Whether we relay transactions to this peer"},
|
||||||
|
{RPCResult::Type::NUM, "last_inv_sequence", "Mempool sequence number of this peer's last INV"},
|
||||||
|
{RPCResult::Type::NUM, "inv_to_send", "How many txs we have queued to announce to this peer"},
|
||||||
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
|
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
|
||||||
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
|
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
|
||||||
{RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
|
{RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
|
||||||
|
@ -238,6 +240,8 @@ static RPCHelpMan getpeerinfo()
|
||||||
obj.pushKV("services", strprintf("%016x", services));
|
obj.pushKV("services", strprintf("%016x", services));
|
||||||
obj.pushKV("servicesnames", GetServicesNames(services));
|
obj.pushKV("servicesnames", GetServicesNames(services));
|
||||||
obj.pushKV("relaytxes", statestats.m_relay_txs);
|
obj.pushKV("relaytxes", statestats.m_relay_txs);
|
||||||
|
obj.pushKV("last_inv_sequence", statestats.m_last_inv_seq);
|
||||||
|
obj.pushKV("inv_to_send", statestats.m_inv_to_send);
|
||||||
obj.pushKV("lastsend", count_seconds(stats.m_last_send));
|
obj.pushKV("lastsend", count_seconds(stats.m_last_send));
|
||||||
obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
|
obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
|
||||||
obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));
|
obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));
|
||||||
|
@ -318,7 +322,7 @@ static RPCHelpMan addnode()
|
||||||
strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
|
strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
|
||||||
" and are counted separately from the -maxconnections limit.\n",
|
" and are counted separately from the -maxconnections limit.\n",
|
||||||
{
|
{
|
||||||
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the peer to connect to"},
|
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address/hostname optionally followed by :port of the peer to connect to"},
|
||||||
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
|
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
|
||||||
{"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
|
{"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
|
||||||
},
|
},
|
||||||
|
@ -1000,26 +1004,28 @@ static RPCHelpMan addpeeraddress()
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
std::optional<CNetAddr> net_addr{LookupHost(addr_string, false)};
|
std::optional<CNetAddr> net_addr{LookupHost(addr_string, false)};
|
||||||
|
if (!net_addr.has_value()) {
|
||||||
|
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Invalid IP address");
|
||||||
|
}
|
||||||
|
|
||||||
bool success{false};
|
bool success{false};
|
||||||
|
|
||||||
if (net_addr.has_value()) {
|
CService service{net_addr.value(), port};
|
||||||
CService service{net_addr.value(), port};
|
CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
|
||||||
CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
|
address.nTime = Now<NodeSeconds>();
|
||||||
address.nTime = Now<NodeSeconds>();
|
// The source address is set equal to the address. This is equivalent to the peer
|
||||||
// The source address is set equal to the address. This is equivalent to the peer
|
// announcing itself.
|
||||||
// announcing itself.
|
if (addrman.Add({address}, address)) {
|
||||||
if (addrman.Add({address}, address)) {
|
success = true;
|
||||||
success = true;
|
if (tried) {
|
||||||
if (tried) {
|
// Attempt to move the address to the tried addresses table.
|
||||||
// Attempt to move the address to the tried addresses table.
|
if (!addrman.Good(address)) {
|
||||||
if (!addrman.Good(address)) {
|
success = false;
|
||||||
success = false;
|
obj.pushKV("error", "failed-adding-to-tried");
|
||||||
obj.pushKV("error", "failed-adding-to-tried");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
obj.pushKV("error", "failed-adding-to-new");
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
obj.pushKV("error", "failed-adding-to-new");
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.pushKV("success", success);
|
obj.pushKV("success", success);
|
||||||
|
|
|
@ -1608,7 +1608,7 @@ static RPCHelpMan finalizepsbt()
|
||||||
return RPCHelpMan{"finalizepsbt",
|
return RPCHelpMan{"finalizepsbt",
|
||||||
"Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
|
"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"
|
"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",
|
"Implements the Finalizer and Extractor roles.\n",
|
||||||
{
|
{
|
||||||
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
|
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <crypto/sha256.h>
|
#include <crypto/sha256.h>
|
||||||
#include <pubkey.h>
|
#include <pubkey.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
typedef std::vector<unsigned char> valtype;
|
typedef std::vector<unsigned char> valtype;
|
||||||
|
@ -197,7 +198,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) {
|
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror) {
|
||||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||||
if (vchSig.size() == 0) {
|
if (vchSig.size() == 0) {
|
||||||
|
@ -214,7 +215,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
|
bool static CheckPubKeyEncoding(const valtype &vchPubKey, script_verify_flags flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||||
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
|
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
|
||||||
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
||||||
}
|
}
|
||||||
|
@ -317,7 +318,7 @@ public:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||||
{
|
{
|
||||||
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
|
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
|
||||||
|
|
||||||
|
@ -343,7 +344,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||||
{
|
{
|
||||||
assert(sigversion == SigVersion::TAPSCRIPT);
|
assert(sigversion == SigVersion::TAPSCRIPT);
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr
|
||||||
* A return value of false means the script fails entirely. When true is returned, the
|
* A return value of false means the script fails entirely. When true is returned, the
|
||||||
* success variable indicates whether the signature check itself succeeded.
|
* success variable indicates whether the signature check itself succeeded.
|
||||||
*/
|
*/
|
||||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||||
{
|
{
|
||||||
switch (sigversion) {
|
switch (sigversion) {
|
||||||
case SigVersion::BASE:
|
case SigVersion::BASE:
|
||||||
|
@ -403,7 +404,7 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||||
{
|
{
|
||||||
static const CScriptNum bnZero(0);
|
static const CScriptNum bnZero(0);
|
||||||
static const CScriptNum bnOne(1);
|
static const CScriptNum bnOne(1);
|
||||||
|
@ -1233,7 +1234,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
||||||
return set_success(serror);
|
return set_success(serror);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||||
{
|
{
|
||||||
ScriptExecutionData execdata;
|
ScriptExecutionData execdata;
|
||||||
return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
|
return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
|
||||||
|
@ -1824,7 +1825,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
|
||||||
template class GenericTransactionSignatureChecker<CTransaction>;
|
template class GenericTransactionSignatureChecker<CTransaction>;
|
||||||
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||||
|
|
||||||
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, script_verify_flags flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||||
{
|
{
|
||||||
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
|
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
|
||||||
|
|
||||||
|
@ -1909,7 +1910,7 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
|
||||||
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||||
{
|
{
|
||||||
CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
|
CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
|
||||||
std::span stack{witness.stack};
|
std::span stack{witness.stack};
|
||||||
|
@ -1994,7 +1995,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
||||||
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
|
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||||
{
|
{
|
||||||
static const CScriptWitness emptyWitness;
|
static const CScriptWitness emptyWitness;
|
||||||
if (witness == nullptr) {
|
if (witness == nullptr) {
|
||||||
|
@ -2131,7 +2132,7 @@ size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& wi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags)
|
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags)
|
||||||
{
|
{
|
||||||
static const CScriptWitness witnessEmpty;
|
static const CScriptWitness witnessEmpty;
|
||||||
|
|
||||||
|
@ -2161,3 +2162,48 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
|
||||||
|
const std::map<std::string, script_verify_flag_name> g_verify_flag_names{
|
||||||
|
FLAG_NAME(P2SH),
|
||||||
|
FLAG_NAME(STRICTENC),
|
||||||
|
FLAG_NAME(DERSIG),
|
||||||
|
FLAG_NAME(LOW_S),
|
||||||
|
FLAG_NAME(SIGPUSHONLY),
|
||||||
|
FLAG_NAME(MINIMALDATA),
|
||||||
|
FLAG_NAME(NULLDUMMY),
|
||||||
|
FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
|
||||||
|
FLAG_NAME(CLEANSTACK),
|
||||||
|
FLAG_NAME(MINIMALIF),
|
||||||
|
FLAG_NAME(NULLFAIL),
|
||||||
|
FLAG_NAME(CHECKLOCKTIMEVERIFY),
|
||||||
|
FLAG_NAME(CHECKSEQUENCEVERIFY),
|
||||||
|
FLAG_NAME(WITNESS),
|
||||||
|
FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
|
||||||
|
FLAG_NAME(WITNESS_PUBKEYTYPE),
|
||||||
|
FLAG_NAME(CONST_SCRIPTCODE),
|
||||||
|
FLAG_NAME(TAPROOT),
|
||||||
|
FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
|
||||||
|
FLAG_NAME(DISCOURAGE_OP_SUCCESS),
|
||||||
|
FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
|
||||||
|
};
|
||||||
|
#undef FLAG_NAME
|
||||||
|
|
||||||
|
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
if (flags == SCRIPT_VERIFY_NONE) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
script_verify_flags leftover = flags;
|
||||||
|
for (const auto& [name, flag] : g_verify_flag_names) {
|
||||||
|
if ((flags & flag) != 0) {
|
||||||
|
res.push_back(name);
|
||||||
|
leftover &= ~flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftover != 0) {
|
||||||
|
res.push_back(strprintf("0x%08x", leftover.as_int()));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <script/script_error.h> // IWYU pragma: export
|
#include <script/script_error.h> // IWYU pragma: export
|
||||||
|
#include <script/verify_flags.h> // IWYU pragma: export
|
||||||
#include <span.h>
|
#include <span.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
|
@ -42,35 +43,36 @@ enum
|
||||||
* All flags are intended to be soft forks: the set of acceptable scripts under
|
* All flags are intended to be soft forks: the set of acceptable scripts under
|
||||||
* flags (A | B) is a subset of the acceptable scripts under flag (A).
|
* flags (A | B) is a subset of the acceptable scripts under flag (A).
|
||||||
*/
|
*/
|
||||||
enum : uint32_t {
|
|
||||||
SCRIPT_VERIFY_NONE = 0,
|
|
||||||
|
|
||||||
|
static constexpr script_verify_flags SCRIPT_VERIFY_NONE{0};
|
||||||
|
|
||||||
|
enum class script_verify_flag_name : uint8_t {
|
||||||
// Evaluate P2SH subscripts (BIP16).
|
// Evaluate P2SH subscripts (BIP16).
|
||||||
SCRIPT_VERIFY_P2SH = (1U << 0),
|
SCRIPT_VERIFY_P2SH,
|
||||||
|
|
||||||
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||||
// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
||||||
// (not used or intended as a consensus rule).
|
// (not used or intended as a consensus rule).
|
||||||
SCRIPT_VERIFY_STRICTENC = (1U << 1),
|
SCRIPT_VERIFY_STRICTENC,
|
||||||
|
|
||||||
// Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
|
// Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
|
||||||
SCRIPT_VERIFY_DERSIG = (1U << 2),
|
SCRIPT_VERIFY_DERSIG,
|
||||||
|
|
||||||
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||||
// (BIP62 rule 5).
|
// (BIP62 rule 5).
|
||||||
SCRIPT_VERIFY_LOW_S = (1U << 3),
|
SCRIPT_VERIFY_LOW_S,
|
||||||
|
|
||||||
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
|
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
|
||||||
SCRIPT_VERIFY_NULLDUMMY = (1U << 4),
|
SCRIPT_VERIFY_NULLDUMMY,
|
||||||
|
|
||||||
// Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
|
// Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
|
||||||
SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5),
|
SCRIPT_VERIFY_SIGPUSHONLY,
|
||||||
|
|
||||||
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
||||||
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
||||||
// any other push causes the script to fail (BIP62 rule 3).
|
// any other push causes the script to fail (BIP62 rule 3).
|
||||||
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
||||||
SCRIPT_VERIFY_MINIMALDATA = (1U << 6),
|
SCRIPT_VERIFY_MINIMALDATA,
|
||||||
|
|
||||||
// Discourage use of NOPs reserved for upgrades (NOP1-10)
|
// Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||||
//
|
//
|
||||||
|
@ -82,7 +84,7 @@ enum : uint32_t {
|
||||||
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
||||||
// NOPs that have associated forks to give them new meaning (CLTV, CSV)
|
// NOPs that have associated forks to give them new meaning (CLTV, CSV)
|
||||||
// are not subject to this rule.
|
// are not subject to this rule.
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7),
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
|
||||||
|
|
||||||
// Require that only a single stack element remains after evaluation. This changes the success criterion from
|
// Require that only a single stack element remains after evaluation. This changes the success criterion from
|
||||||
// "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
// "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
||||||
|
@ -91,64 +93,72 @@ enum : uint32_t {
|
||||||
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
|
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
|
||||||
// Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
|
// Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
|
||||||
// consensus rules. It is automatic there and does not need this flag.
|
// consensus rules. It is automatic there and does not need this flag.
|
||||||
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
|
SCRIPT_VERIFY_CLEANSTACK,
|
||||||
|
|
||||||
// Verify CHECKLOCKTIMEVERIFY
|
// Verify CHECKLOCKTIMEVERIFY
|
||||||
//
|
//
|
||||||
// See BIP65 for details.
|
// See BIP65 for details.
|
||||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
||||||
|
|
||||||
// support CHECKSEQUENCEVERIFY opcode
|
// support CHECKSEQUENCEVERIFY opcode
|
||||||
//
|
//
|
||||||
// See BIP112 for details
|
// See BIP112 for details
|
||||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10),
|
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
||||||
|
|
||||||
// Support segregated witness
|
// Support segregated witness
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_WITNESS = (1U << 11),
|
SCRIPT_VERIFY_WITNESS,
|
||||||
|
|
||||||
// Making v1-v16 witness program non-standard
|
// Making v1-v16 witness program non-standard
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12),
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
|
||||||
|
|
||||||
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
||||||
//
|
//
|
||||||
// Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
|
// Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
|
||||||
// rules. It is automatic there and does not depend on this flag.
|
// rules. It is automatic there and does not depend on this flag.
|
||||||
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
|
SCRIPT_VERIFY_MINIMALIF,
|
||||||
|
|
||||||
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
|
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
|
SCRIPT_VERIFY_NULLFAIL,
|
||||||
|
|
||||||
// Public keys in segregated witness scripts must be compressed
|
// Public keys in segregated witness scripts must be compressed
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
|
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
|
||||||
|
|
||||||
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
|
SCRIPT_VERIFY_CONST_SCRIPTCODE,
|
||||||
|
|
||||||
// Taproot/Tapscript validation (BIPs 341 & 342)
|
// Taproot/Tapscript validation (BIPs 341 & 342)
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_TAPROOT = (1U << 17),
|
SCRIPT_VERIFY_TAPROOT,
|
||||||
|
|
||||||
// Making unknown Taproot leaf versions non-standard
|
// Making unknown Taproot leaf versions non-standard
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
|
||||||
|
|
||||||
// Making unknown OP_SUCCESS non-standard
|
// Making unknown OP_SUCCESS non-standard
|
||||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
|
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS,
|
||||||
|
|
||||||
// Making unknown public key versions (in BIP 342 scripts) non-standard
|
// Making unknown public key versions (in BIP 342 scripts) non-standard
|
||||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
|
||||||
|
|
||||||
// Constants to point to the highest flag in use. Add new flags above this line.
|
// Constants to point to the highest flag in use. Add new flags above this line.
|
||||||
//
|
//
|
||||||
SCRIPT_VERIFY_END_MARKER
|
SCRIPT_VERIFY_END_MARKER
|
||||||
};
|
};
|
||||||
|
using enum script_verify_flag_name;
|
||||||
|
|
||||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
static constexpr int MAX_SCRIPT_VERIFY_FLAGS_BITS = static_cast<int>(SCRIPT_VERIFY_END_MARKER);
|
||||||
|
|
||||||
|
// assert there is still a spare bit
|
||||||
|
static_assert(0 < MAX_SCRIPT_VERIFY_FLAGS_BITS && MAX_SCRIPT_VERIFY_FLAGS_BITS <= 63);
|
||||||
|
|
||||||
|
static constexpr script_verify_flags::value_type MAX_SCRIPT_VERIFY_FLAGS = ((script_verify_flags::value_type{1} << MAX_SCRIPT_VERIFY_FLAGS_BITS) - 1);
|
||||||
|
|
||||||
|
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror);
|
||||||
|
|
||||||
struct PrecomputedTransactionData
|
struct PrecomputedTransactionData
|
||||||
{
|
{
|
||||||
|
@ -363,12 +373,16 @@ uint256 ComputeTapbranchHash(std::span<const unsigned char> a, std::span<const u
|
||||||
* Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
|
* Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
|
||||||
uint256 ComputeTaprootMerkleRoot(std::span<const unsigned char> control, const uint256& tapleaf_hash);
|
uint256 ComputeTaprootMerkleRoot(std::span<const unsigned char> control, const uint256& tapleaf_hash);
|
||||||
|
|
||||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||||
|
|
||||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
|
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags);
|
||||||
|
|
||||||
int FindAndDelete(CScript& script, const CScript& b);
|
int FindAndDelete(CScript& script, const CScript& b);
|
||||||
|
|
||||||
|
extern const std::map<std::string, script_verify_flag_name> g_verify_flag_names;
|
||||||
|
|
||||||
|
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags);
|
||||||
|
|
||||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-present The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||||
|
#define BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class script_verify_flag_name : uint8_t;
|
||||||
|
|
||||||
|
class script_verify_flags
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = uint64_t;
|
||||||
|
|
||||||
|
consteval script_verify_flags() = default;
|
||||||
|
|
||||||
|
// also allow construction with hard-coded 0 (but not other integers)
|
||||||
|
consteval explicit(false) script_verify_flags(value_type f) : m_value{f} { if (f != 0) throw 0; }
|
||||||
|
|
||||||
|
// implicit construction from a hard-coded SCRIPT_VERIFY_* constant is also okay
|
||||||
|
constexpr explicit(false) script_verify_flags(script_verify_flag_name f) : m_value{value_type{1} << static_cast<uint8_t>(f)} { }
|
||||||
|
|
||||||
|
// rule of 5
|
||||||
|
constexpr script_verify_flags(const script_verify_flags&) = default;
|
||||||
|
constexpr script_verify_flags(script_verify_flags&&) = default;
|
||||||
|
constexpr script_verify_flags& operator=(const script_verify_flags&) = default;
|
||||||
|
constexpr script_verify_flags& operator=(script_verify_flags&&) = default;
|
||||||
|
constexpr ~script_verify_flags() = default;
|
||||||
|
|
||||||
|
// integer conversion needs to be very explicit
|
||||||
|
static constexpr script_verify_flags from_int(value_type f) { script_verify_flags r; r.m_value = f; return r; }
|
||||||
|
constexpr value_type as_int() const { return m_value; }
|
||||||
|
|
||||||
|
// bitwise operations
|
||||||
|
constexpr script_verify_flags operator~() const { return from_int(~m_value); }
|
||||||
|
friend constexpr script_verify_flags operator|(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value | b.m_value); }
|
||||||
|
friend constexpr script_verify_flags operator&(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value & b.m_value); }
|
||||||
|
|
||||||
|
// in-place bitwise operations
|
||||||
|
constexpr script_verify_flags& operator|=(script_verify_flags vf) { m_value |= vf.m_value; return *this; }
|
||||||
|
constexpr script_verify_flags& operator&=(script_verify_flags vf) { m_value &= vf.m_value; return *this; }
|
||||||
|
|
||||||
|
// tests
|
||||||
|
constexpr explicit operator bool() const { return m_value != 0; }
|
||||||
|
constexpr bool operator==(script_verify_flags other) const { return m_value == other.m_value; }
|
||||||
|
|
||||||
|
/** Compare two script_verify_flags. <, >, <=, and >= are auto-generated from this. */
|
||||||
|
friend constexpr std::strong_ordering operator<=>(const script_verify_flags& a, const script_verify_flags& b) noexcept
|
||||||
|
{
|
||||||
|
return a.m_value <=> b.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type m_value{0}; // default value is SCRIPT_VERIFY_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr script_verify_flags operator~(script_verify_flag_name f)
|
||||||
|
{
|
||||||
|
return ~script_verify_flags{f};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr script_verify_flags operator|(script_verify_flag_name f1, script_verify_flag_name f2)
|
||||||
|
{
|
||||||
|
return script_verify_flags{f1} | f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
|
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
|
||||||
|
|
||||||
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
static constexpr script_verify_flags BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
||||||
|
|
||||||
static bool FetchAndClearCommitmentSection(const std::span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
|
static bool FetchAndClearCommitmentSection(const std::span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,6 +25,8 @@ add_executable(test_bitcoin
|
||||||
blockmanager_tests.cpp
|
blockmanager_tests.cpp
|
||||||
bloom_tests.cpp
|
bloom_tests.cpp
|
||||||
bswap_tests.cpp
|
bswap_tests.cpp
|
||||||
|
caches_tests.cpp
|
||||||
|
chain_tests.cpp
|
||||||
chainstate_write_tests.cpp
|
chainstate_write_tests.cpp
|
||||||
checkqueue_tests.cpp
|
checkqueue_tests.cpp
|
||||||
cluster_linearize_tests.cpp
|
cluster_linearize_tests.cpp
|
||||||
|
@ -101,6 +103,7 @@ add_executable(test_bitcoin
|
||||||
span_tests.cpp
|
span_tests.cpp
|
||||||
streams_tests.cpp
|
streams_tests.cpp
|
||||||
sync_tests.cpp
|
sync_tests.cpp
|
||||||
|
system_ram_tests.cpp
|
||||||
system_tests.cpp
|
system_tests.cpp
|
||||||
testnet4_miner_tests.cpp
|
testnet4_miner_tests.cpp
|
||||||
timeoffsets_tests.cpp
|
timeoffsets_tests.cpp
|
||||||
|
@ -196,7 +199,10 @@ function(add_boost_test source_file)
|
||||||
COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT
|
COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT
|
||||||
)
|
)
|
||||||
set_property(TEST ${test_suite_name} PROPERTY
|
set_property(TEST ${test_suite_name} PROPERTY
|
||||||
SKIP_REGULAR_EXPRESSION "no test cases matching filter" "skipping script_assets_test"
|
SKIP_REGULAR_EXPRESSION
|
||||||
|
"no test cases matching filter"
|
||||||
|
"skipping script_assets_test"
|
||||||
|
"skipping total_ram"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
|
@ -459,10 +459,16 @@ BOOST_AUTO_TEST_CASE(getaddr_unfiltered)
|
||||||
addrman->Attempt(addr3, /*fCountFailure=*/true, /*time=*/Now<NodeSeconds>() - 61s);
|
addrman->Attempt(addr3, /*fCountFailure=*/true, /*time=*/Now<NodeSeconds>() - 61s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set time more than 10 minutes in the future (flying DeLorean), so this
|
||||||
|
// addr should be isTerrible = true
|
||||||
|
CAddress addr4 = CAddress(ResolveService("250.252.2.4", 9997), NODE_NONE);
|
||||||
|
addr4.nTime = Now<NodeSeconds>() + 11min;
|
||||||
|
BOOST_CHECK(addrman->Add({addr4}, source));
|
||||||
|
|
||||||
// GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
|
// GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
|
||||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
|
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
|
||||||
// Unfiltered GetAddr should return all addrs
|
// Unfiltered GetAddr should return all addrs
|
||||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 3U);
|
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 4U);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include <node/caches.h>
|
||||||
|
#include <util/byte_units.h>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
using namespace node;
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(caches_tests)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(oversized_dbcache_warning)
|
||||||
|
{
|
||||||
|
// memory restricted setup - cap is DEFAULT_DB_CACHE (450 MiB)
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/4_MiB, /*total_ram=*/1024_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/512_MiB, /*total_ram=*/1024_MiB)); // At cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/1024_MiB)); // Over cap
|
||||||
|
|
||||||
|
// 2 GiB RAM - cap is 75%
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/2048_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1600_MiB, /*total_ram=*/2048_MiB)); // Over cap
|
||||||
|
|
||||||
|
if constexpr (SIZE_MAX == UINT64_MAX) {
|
||||||
|
// 4 GiB RAM - cap is 75%
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/2500_MiB, /*total_ram=*/4096_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/3500_MiB, /*total_ram=*/4096_MiB)); // Over cap
|
||||||
|
|
||||||
|
// 8 GiB RAM - cap is 75%
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/6000_MiB, /*total_ram=*/8192_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/7000_MiB, /*total_ram=*/8192_MiB)); // Over cap
|
||||||
|
|
||||||
|
// 16 GiB RAM - cap is 75%
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/10'000_MiB, /*total_ram=*/16384_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/15'000_MiB, /*total_ram=*/16384_MiB)); // Over cap
|
||||||
|
|
||||||
|
// 32 GiB RAM - cap is 75%
|
||||||
|
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/20'000_MiB, /*total_ram=*/32768_MiB)); // Under cap
|
||||||
|
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/30'000_MiB, /*total_ram=*/32768_MiB)); // Over cap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright (c) The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
|
#include <test/util/setup_common.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(chain_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const CBlockIndex* NaiveGetAncestor(const CBlockIndex* a, int height)
|
||||||
|
{
|
||||||
|
while (a->nHeight > height) {
|
||||||
|
a = a->pprev;
|
||||||
|
}
|
||||||
|
BOOST_REQUIRE_EQUAL(a->nHeight, height);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CBlockIndex* NaiveLastCommonAncestor(const CBlockIndex* a, const CBlockIndex* b)
|
||||||
|
{
|
||||||
|
while (a->nHeight > b->nHeight) {
|
||||||
|
a = a->pprev;
|
||||||
|
}
|
||||||
|
while (b->nHeight > a->nHeight) {
|
||||||
|
b = b->pprev;
|
||||||
|
}
|
||||||
|
while (a != b) {
|
||||||
|
BOOST_REQUIRE_EQUAL(a->nHeight, b->nHeight);
|
||||||
|
a = a->pprev;
|
||||||
|
b = b->pprev;
|
||||||
|
}
|
||||||
|
BOOST_REQUIRE_EQUAL(a, b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(chain_test)
|
||||||
|
{
|
||||||
|
FastRandomContext ctx;
|
||||||
|
std::vector<std::unique_ptr<CBlockIndex>> block_index;
|
||||||
|
// Run 10 iterations of the whole test.
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
block_index.clear();
|
||||||
|
// Create genesis block.
|
||||||
|
auto genesis = std::make_unique<CBlockIndex>();
|
||||||
|
genesis->nHeight = 0;
|
||||||
|
block_index.push_back(std::move(genesis));
|
||||||
|
// Create 10000 more blocks.
|
||||||
|
for (int b = 0; b < 10000; ++b) {
|
||||||
|
auto new_index = std::make_unique<CBlockIndex>();
|
||||||
|
// 95% of blocks build on top of the last block; the others fork off randomly.
|
||||||
|
if (ctx.randrange(20) != 0) {
|
||||||
|
new_index->pprev = block_index.back().get();
|
||||||
|
} else {
|
||||||
|
new_index->pprev = block_index[ctx.randrange(block_index.size())].get();
|
||||||
|
}
|
||||||
|
new_index->nHeight = new_index->pprev->nHeight + 1;
|
||||||
|
new_index->BuildSkip();
|
||||||
|
block_index.push_back(std::move(new_index));
|
||||||
|
}
|
||||||
|
// Run 10000 random GetAncestor queries.
|
||||||
|
for (int q = 0; q < 10000; ++q) {
|
||||||
|
const CBlockIndex* block = block_index[ctx.randrange(block_index.size())].get();
|
||||||
|
unsigned height = ctx.randrange<unsigned>(block->nHeight + 1);
|
||||||
|
const CBlockIndex* result = block->GetAncestor(height);
|
||||||
|
BOOST_CHECK(result == NaiveGetAncestor(block, height));
|
||||||
|
}
|
||||||
|
// Run 10000 random LastCommonAncestor queries.
|
||||||
|
for (int q = 0; q < 10000; ++q) {
|
||||||
|
const CBlockIndex* block1 = block_index[ctx.randrange(block_index.size())].get();
|
||||||
|
const CBlockIndex* block2 = block_index[ctx.randrange(block_index.size())].get();
|
||||||
|
const CBlockIndex* result = LastCommonAncestor(block1, block2);
|
||||||
|
BOOST_CHECK(result == NaiveLastCommonAncestor(block1, block2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -62,7 +62,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||||
/*inbound_onion=*/false};
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/0};
|
||||||
|
|
||||||
connman.Handshake(
|
connman.Handshake(
|
||||||
/*node=*/dummyNode1,
|
/*node=*/dummyNode1,
|
||||||
|
@ -128,7 +129,8 @@ void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager&
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
connType,
|
connType,
|
||||||
/*inbound_onion=*/false});
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/0});
|
||||||
CNode &node = *vNodes.back();
|
CNode &node = *vNodes.back();
|
||||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||||
|
|
||||||
|
@ -327,7 +329,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::INBOUND,
|
ConnectionType::INBOUND,
|
||||||
/*inbound_onion=*/false};
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/1};
|
||||||
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
|
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
|
||||||
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
|
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
|
||||||
nodes[0]->fSuccessfullyConnected = true;
|
nodes[0]->fSuccessfullyConnected = true;
|
||||||
|
@ -347,7 +350,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::INBOUND,
|
ConnectionType::INBOUND,
|
||||||
/*inbound_onion=*/false};
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/1};
|
||||||
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
|
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
|
||||||
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
|
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
|
||||||
nodes[1]->fSuccessfullyConnected = true;
|
nodes[1]->fSuccessfullyConnected = true;
|
||||||
|
@ -377,7 +381,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||||
/*inbound_onion=*/false};
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/2};
|
||||||
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
|
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
|
||||||
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
|
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
|
||||||
nodes[2]->fSuccessfullyConnected = true;
|
nodes[2]->fSuccessfullyConnected = true;
|
||||||
|
@ -419,7 +424,8 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
||||||
CAddress(),
|
CAddress(),
|
||||||
/*addrNameIn=*/"",
|
/*addrNameIn=*/"",
|
||||||
ConnectionType::INBOUND,
|
ConnectionType::INBOUND,
|
||||||
/*inbound_onion=*/false};
|
/*inbound_onion=*/false,
|
||||||
|
/*network_key=*/1};
|
||||||
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
|
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
|
||||||
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
|
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
|
||||||
dummyNode.fSuccessfullyConnected = true;
|
dummyNode.fSuccessfullyConnected = true;
|
||||||
|
|
|
@ -288,7 +288,7 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend
|
||||||
// consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
|
// consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto flags{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||||
if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
|
if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
|
||||||
// Avoid:
|
// Avoid:
|
||||||
// script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
|
// script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
#include <net_processing.h>
|
||||||
#include <netaddress.h>
|
#include <netaddress.h>
|
||||||
#include <protocol.h>
|
#include <protocol.h>
|
||||||
#include <test/fuzz/FuzzedDataProvider.h>
|
#include <test/fuzz/FuzzedDataProvider.h>
|
||||||
#include <test/fuzz/fuzz.h>
|
#include <test/fuzz/fuzz.h>
|
||||||
#include <test/fuzz/util.h>
|
#include <test/fuzz/util.h>
|
||||||
#include <test/fuzz/util/net.h>
|
#include <test/fuzz/util/net.h>
|
||||||
|
#include <test/fuzz/util/threadinterrupt.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
|
|
||||||
|
@ -51,19 +53,36 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddrManDeterministic& addr_man{*addr_man_ptr};
|
AddrManDeterministic& addr_man{*addr_man_ptr};
|
||||||
|
auto net_events{ConsumeNetEvents(fuzzed_data_provider)};
|
||||||
|
|
||||||
|
// Mock CreateSock() to create FuzzedSock.
|
||||||
|
auto CreateSockOrig = CreateSock;
|
||||||
|
CreateSock = [&fuzzed_data_provider](int, int, int) {
|
||||||
|
return std::make_unique<FuzzedSock>(fuzzed_data_provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock g_dns_lookup() to return a fuzzed address.
|
||||||
|
auto g_dns_lookup_orig = g_dns_lookup;
|
||||||
|
g_dns_lookup = [&fuzzed_data_provider](const std::string&, bool) {
|
||||||
|
return std::vector<CNetAddr>{ConsumeNetAddr(fuzzed_data_provider)};
|
||||||
|
};
|
||||||
|
|
||||||
ConnmanTestMsg connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
|
ConnmanTestMsg connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
|
||||||
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
|
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
|
||||||
addr_man,
|
addr_man,
|
||||||
netgroupman,
|
netgroupman,
|
||||||
Params(),
|
Params(),
|
||||||
fuzzed_data_provider.ConsumeBool()};
|
fuzzed_data_provider.ConsumeBool(),
|
||||||
|
ConsumeThreadInterrupt(fuzzed_data_provider)};
|
||||||
|
|
||||||
const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
|
const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
|
||||||
CConnman::Options options;
|
CConnman::Options options;
|
||||||
|
options.m_msgproc = &net_events;
|
||||||
options.nMaxOutboundLimit = max_outbound_limit;
|
options.nMaxOutboundLimit = max_outbound_limit;
|
||||||
connman.Init(options);
|
connman.Init(options);
|
||||||
|
|
||||||
CNetAddr random_netaddr;
|
CNetAddr random_netaddr;
|
||||||
|
CAddress random_address;
|
||||||
CNode random_node = ConsumeNode(fuzzed_data_provider);
|
CNode random_node = ConsumeNode(fuzzed_data_provider);
|
||||||
CSubNet random_subnet;
|
CSubNet random_subnet;
|
||||||
std::string random_string;
|
std::string random_string;
|
||||||
|
@ -79,6 +98,9 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
||||||
[&] {
|
[&] {
|
||||||
random_netaddr = ConsumeNetAddr(fuzzed_data_provider);
|
random_netaddr = ConsumeNetAddr(fuzzed_data_provider);
|
||||||
},
|
},
|
||||||
|
[&] {
|
||||||
|
random_address = ConsumeAddress(fuzzed_data_provider);
|
||||||
|
},
|
||||||
[&] {
|
[&] {
|
||||||
random_subnet = ConsumeSubNet(fuzzed_data_provider);
|
random_subnet = ConsumeSubNet(fuzzed_data_provider);
|
||||||
},
|
},
|
||||||
|
@ -143,6 +165,52 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
|
connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
ConnectionType conn_type{
|
||||||
|
fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES)};
|
||||||
|
if (conn_type == ConnectionType::INBOUND) { // INBOUND is not allowed
|
||||||
|
conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
connman.OpenNetworkConnection(
|
||||||
|
/*addrConnect=*/random_address,
|
||||||
|
/*fCountFailure=*/fuzzed_data_provider.ConsumeBool(),
|
||||||
|
/*grant_outbound=*/{},
|
||||||
|
/*pszDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
|
||||||
|
/*conn_type=*/conn_type,
|
||||||
|
/*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
|
||||||
|
const auto peer = ConsumeAddress(fuzzed_data_provider);
|
||||||
|
connman.CreateNodeFromAcceptedSocketPublic(
|
||||||
|
/*sock=*/CreateSock(AF_INET, SOCK_STREAM, IPPROTO_TCP),
|
||||||
|
/*permissions=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS),
|
||||||
|
/*addr_bind=*/ConsumeAddress(fuzzed_data_provider),
|
||||||
|
/*addr_peer=*/peer);
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
CConnman::Options options;
|
||||||
|
|
||||||
|
options.vBinds = ConsumeServiceVector(fuzzed_data_provider);
|
||||||
|
|
||||||
|
options.vWhiteBinds = std::vector<NetWhitebindPermissions>{
|
||||||
|
fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 5)};
|
||||||
|
for (auto& wb : options.vWhiteBinds) {
|
||||||
|
wb.m_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
|
||||||
|
wb.m_service = ConsumeService(fuzzed_data_provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.onion_binds = ConsumeServiceVector(fuzzed_data_provider);
|
||||||
|
|
||||||
|
options.bind_on_any = options.vBinds.empty() && options.vWhiteBinds.empty() &&
|
||||||
|
options.onion_binds.empty();
|
||||||
|
|
||||||
|
connman.InitBindsPublic(options);
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
connman.SocketHandlerPublic();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(void)connman.GetAddedNodeInfo(fuzzed_data_provider.ConsumeBool());
|
(void)connman.GetAddedNodeInfo(fuzzed_data_provider.ConsumeBool());
|
||||||
|
@ -162,4 +230,6 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
||||||
(void)connman.ASMapHealthCheck();
|
(void)connman.ASMapHealthCheck();
|
||||||
|
|
||||||
connman.ClearTestNodes();
|
connman.ClearTestNodes();
|
||||||
|
g_dns_lookup = g_dns_lookup_orig;
|
||||||
|
CreateSock = CreateSockOrig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
FUZZ_TARGET(eval_script)
|
FUZZ_TARGET(eval_script)
|
||||||
{
|
{
|
||||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||||
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||||
const std::vector<uint8_t> script_bytes = [&] {
|
const std::vector<uint8_t> script_bytes = [&] {
|
||||||
if (fuzzed_data_provider.remaining_bytes() != 0) {
|
if (fuzzed_data_provider.remaining_bytes() != 0) {
|
||||||
return fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
|
return fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
|
||||||
|
|
|
@ -75,7 +75,7 @@ auto& FuzzTargets()
|
||||||
|
|
||||||
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
|
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
|
||||||
{
|
{
|
||||||
const auto [it, ins]{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after Apple-Clang-16 ? */ {std::move(target), std::move(opts)})};
|
const auto [it, ins]{FuzzTargets().try_emplace(name, std::move(target), std::move(opts))};
|
||||||
Assert(ins);
|
Assert(ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <test/fuzz/fuzz.h>
|
#include <test/fuzz/fuzz.h>
|
||||||
#include <test/fuzz/util.h>
|
#include <test/fuzz/util.h>
|
||||||
#include <test/fuzz/util/net.h>
|
#include <test/fuzz/util/net.h>
|
||||||
|
#include <test/fuzz/util/threadinterrupt.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <util/fs_helpers.h>
|
#include <util/fs_helpers.h>
|
||||||
#include <util/threadinterrupt.h>
|
#include <util/threadinterrupt.h>
|
||||||
|
@ -35,15 +36,15 @@ FUZZ_TARGET(i2p, .init = initialize_i2p)
|
||||||
const fs::path private_key_path = gArgs.GetDataDirNet() / "fuzzed_i2p_private_key";
|
const fs::path private_key_path = gArgs.GetDataDirNet() / "fuzzed_i2p_private_key";
|
||||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), 7656};
|
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), 7656};
|
||||||
const Proxy sam_proxy{addr, /*tor_stream_isolation=*/false};
|
const Proxy sam_proxy{addr, /*tor_stream_isolation=*/false};
|
||||||
CThreadInterrupt interrupt;
|
auto interrupt{ConsumeThreadInterrupt(fuzzed_data_provider)};
|
||||||
|
|
||||||
i2p::sam::Session session{private_key_path, sam_proxy, &interrupt};
|
i2p::sam::Session session{private_key_path, sam_proxy, interrupt};
|
||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
|
|
||||||
if (session.Listen(conn)) {
|
if (session.Listen(conn)) {
|
||||||
if (session.Accept(conn)) {
|
if (session.Accept(conn)) {
|
||||||
try {
|
try {
|
||||||
(void)conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
|
(void)conn.sock->RecvUntilTerminator('\n', 10ms, *interrupt, i2p::sam::MAX_MSG_SIZE);
|
||||||
} catch (const std::runtime_error&) {
|
} catch (const std::runtime_error&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +54,7 @@ FUZZ_TARGET(i2p, .init = initialize_i2p)
|
||||||
|
|
||||||
if (session.Connect(CService{}, conn, proxy_error)) {
|
if (session.Connect(CService{}, conn, proxy_error)) {
|
||||||
try {
|
try {
|
||||||
conn.sock->SendComplete("verack\n", 10ms, interrupt);
|
conn.sock->SendComplete("verack\n", 10ms, *interrupt);
|
||||||
} catch (const std::runtime_error&) {
|
} catch (const std::runtime_error&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
|
||||||
std::deque<COutPoint> available_coins = g_available_coins;
|
std::deque<COutPoint> available_coins = g_available_coins;
|
||||||
LOCK2(::cs_main, pool.cs);
|
LOCK2(::cs_main, pool.cs);
|
||||||
// Cluster size cannot exceed 500
|
// Cluster size cannot exceed 500
|
||||||
LIMITED_WHILE(!available_coins.empty(), 500)
|
LIMITED_WHILE(!available_coins.empty(), 100)
|
||||||
{
|
{
|
||||||
CMutableTransaction mtx = CMutableTransaction();
|
CMutableTransaction mtx = CMutableTransaction();
|
||||||
const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
|
const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
|
||||||
|
|
|
@ -70,7 +70,7 @@ void HeadersSyncSetup::ResetAndInitialize()
|
||||||
|
|
||||||
for (auto conn_type : conn_types) {
|
for (auto conn_type : conn_types) {
|
||||||
CAddress addr{};
|
CAddress addr{};
|
||||||
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
|
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));
|
||||||
CNode& p2p_node = *m_connections.back();
|
CNode& p2p_node = *m_connections.back();
|
||||||
|
|
||||||
connman.Handshake(
|
connman.Handshake(
|
||||||
|
|
|
@ -325,7 +325,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
|
||||||
return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
|
return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
|
||||||
|
|
||||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
|
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
|
||||||
/*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit));
|
/*bypass_limits=*/false, /*test_accept=*/!single_submit));
|
||||||
|
|
||||||
if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
|
if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
|
||||||
// We don't know anything about the validity since transactions were randomly generated, so
|
// We don't know anything about the validity since transactions were randomly generated, so
|
||||||
|
|
|
@ -118,8 +118,8 @@ FUZZ_TARGET(script, .init = initialize_script)
|
||||||
(void)FindAndDelete(script_mut, *other_script);
|
(void)FindAndDelete(script_mut, *other_script);
|
||||||
}
|
}
|
||||||
const std::vector<std::string> random_string_vector = ConsumeRandomLengthStringVector(fuzzed_data_provider);
|
const std::vector<std::string> random_string_vector = ConsumeRandomLengthStringVector(fuzzed_data_provider);
|
||||||
const uint32_t u32{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
const auto flags_rand{fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>()};
|
||||||
const uint32_t flags{u32 | SCRIPT_VERIFY_P2SH};
|
const auto flags = script_verify_flags::from_int(flags_rand) | SCRIPT_VERIFY_P2SH;
|
||||||
{
|
{
|
||||||
CScriptWitness wit;
|
CScriptWitness wit;
|
||||||
for (const auto& s : random_string_vector) {
|
for (const auto& s : random_string_vector) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue