Compare commits

...

69 Commits

Author SHA1 Message Date
fanquake e4c04f7759
ci: add libcpp hardening flags to macOS fuzz job
Follows up to
https://github.com/bitcoin/bitcoin/pull/33425#issuecomment-3323149107.
2025-10-08 10:42:25 +01:00
Ava Chow b510893d00
Merge bitcoin/bitcoin#33494: depends: Update URL for `qrencode` package source tarball
93a70a42d3 depends: Update URL for `qrencode` package source tarball (Hennadii Stepanov)
6de8051263 depends: Use hash instead of file name for package download stamp (Hennadii Stepanov)
46135d90ea depends: Drop redundant check for downloaded file (Hennadii Stepanov)
771978952a depends: Fix `$(package)_fetched` target (Hennadii Stepanov)

Pull request description:

  The https://fukuchi.org/ homepage no longer links to the source tarball, and previously available files appear to have been removed. The homepage now instructs users to download source tarballs from the GitHub [releases](https://github.com/fukuchi/libqrencode/releases) page instead.

  The diff between the source trees is immaterial:
  ```diff
  --- old
  +++ new
  @@ -1,19 +1,16 @@
   27e7deccd2925c94e4190ee64794a051199f215f145f76fd664cdebedbbf8a35  acinclude.m4
  -e1e35b1309482f699a9700a2065a0bce09c2108dd1f78ba7bfbe0f7f0bdcd2e6  aclocal.m4
   a9308eec78790720dbcd5452ab8f241b5f1c6939ccf3389917b8e78cb2b58c9e  autogen.sh
   aa36725d577048f0370dc7415a1acb578fbdfb531c1b384a836d9360a81f6f5c  bitstream.c
   3feaacd7d096834fc5956215598564ec287ad443185c4433c3f8007cc53ceaa6  bitstream.h
   21bd5a34c90d3d6ee540ceb48c3d5aea5f21bd6b829ef3112db832af0bd423d8  ChangeLog
   f8a7a94c9622fab721df47e8121533ebfcb79885aca01ecec2fff00dfb84caef  cmake/FindIconv.cmake
   6345e7eecb92473f361a8eb98dd373aa09cae79a43408cf4b42b00b411c9c197  CMakeLists.txt
  -031d560570eab2eda57c2f9ee9952445002f8bf4a23965fcaf43bbc2c61590e2  config.h.in
  -d2f42cc5771b69f2d2ecd4b31509864ae2b18b25c823986390bb372ee07030fa  configure
   02867a8ea08206c84d5f4c05f41a15c639291091e3dfac27f3a9029d8f5d3028  configure.ac
   a9bdde5616ecdd1e980b44f360600ee8783b1f99b8cc83a2beb163a0a390e861  COPYING
   76c41754bccbf69a60fb7833776637c60b86f59104705c0a5cea9ee3a2968f3d  Doxyfile
   36d84f714cf28397b02d6c44860106a7cb858fc6d25239a2698a72fa5136e5c3  libqrencode.pc.in
   f0ce93a7e1b1f0fab87dd071ec3c8dd80a567d778dfc5930d9f375d676ecb9a0  Makefile.am
  -507c2385fb49c5724e8e3dfca97feb24cbdd4e651f3f0bfa62524e60088091a0  Makefile.in
  +dcb782b1b382328c0bce9194944bdcc65a6035d8e9f89e39436ed2e55ae8b969  makeREADME.sh
   6046d347c6c564fc13a24dc0a15a09a83023e00a4e0d0f23029a81f86ac4d024  mask.c
   9f853cee7d72191a8dbf018b7a86c0f0b6ca661e27a51fab677af911f2ff9e7e  mask.h
   a4e17b68d8db573e152132ebcbdc837b55415c12027f81232662db645faf79f0  mmask.c
  @@ -30,7 +27,7 @@
   6e9ff66002b4a839c6e78bcd6a55342d9eb8b289273fa838441b27ee9969e293  qrinput.h
   68831e02ee1ba602b1937328abec000e616c4472d9dc40067dd45ab7072df172  qrspec.c
   a2a9a5af4d62015e82b48b8316aea2b70031dcdc1ed2b829c0102ea5dc02aca2  qrspec.h
  -8730d006f1d45b90dc0ef9a20e4119420f15b37a09483c929baa4225e21f7900  README
  +a56773b55989ea5cae8a43f2d845ce0afa9576a26170937fbe36a69fee953cd4  README.md
   ace480f2e16001e276c73a5e965c282915ad81e2c28cce3ef574b5a8db7210e8  rsecc.c
   c731ebe26d58d5e5df3b2d694de0fb1c4ae80a36cf0559c49b04e989dee8b182  rsecc.h
   8ed1af4414b5628845519581c82bf51ba2b4dee1f8352c1fd01b50afcd0a0ed7  split.c
  @@ -46,7 +43,6 @@
   e26b20198a7393b3c060891876fa45edb81488aef9df6d6cf45893a605e5e5e4  tests/decoder.h
   adabfbddb0f25de2e2cae970195fcfdf11ad1fa66ce64e237d83f821346f64c4  tests/frame
   23c283a3d3b5f1bbb3108603ebf05d7a95951ba86a288e09140eab612a5eb258  tests/Makefile.am
  -c8b819bf7f00c979ab0b6f0d0fb10289d2006bc6acda1737d2f1c383a60e1e66  tests/Makefile.in
   ebd71b937d4d39f382f8c57f362a01f11fd617b66dbdf6f1a3c4897c8c42e235  tests/prof_qrencode.c
   d4adcb234d4c31473cd9ffedd1bfa9645ba336d426640ba5893dfe6e75db171d  tests/pthread_qrencode.c
   62298641504b0a2c1d199cd7f656cb376959200169042de76f7c5950d2d42395  tests/rscode.c
  @@ -63,6 +59,7 @@
   cf5792cbfb92ba46f7f5f14b98f00813b0e7e4f5e7c790c6a28793774474bf26  tests/test_monkey.c
   3a2c58346d57f6bb2a634d6febf3c65ac524259a2d789657f8d60c678bf8d658  tests/test_mqrspec.c
   b9d8c569ba36a2258cb1ad0d1f85ce4fb2935fad519e902bade59aa9772321aa  tests/test_qrencode.c
  +785fe14a6f8bc096b20ec271771fb09f22f29eb4f9d729f5b40d6b4cc824ce36  tests/test_qrenc.sh
   d0eee6eddf98ee4595c07f7da40c7de548651bf839b26995756e94db2599451f  tests/test_qrinput.c
   ace7885e435ef77cc127da0ac23c724498aaa8d80d53b908063f79c9f0b7acce  tests/test_qrspec.c
   fde9c2735ce94be51e1b0bbffe65415aa3afd2da5bd4c0e2c5e50e1a2f1ea3f7  tests/test_rs.c
  @@ -71,12 +68,4 @@
   6f35c0e1235b31d0068c4ab175d8110c736e60df0309d4be7b3e57dd62d316f6  tests/URI_testset.inc
   6be3983fc397cd5dade1dd219ad6cbe7977f416410b1509984006ecec51605b5  tests/view_qrcode.c
   92b5be1ca2239399232d51503715c848dae9bf3db71b1f03157bfa9779826910  TODO
  -c8af04e62bad4ab75dafd22119026e5e3943f385bdcbe7731a4938102453754c  use/compile
  -1d9048b0ac9d4d1dfce7aa4e3a0b59ccfd32db2f7693814aa6f4c778560f5669  use/config.guess
   b522487f9c47661d321367d133f3d41247dd16d435f2d4b9c643dee95bf65eee  use/config.rpath
  -5bf0da2576ebb21ab60a9d9291a85b40af0e956a9eafb709ca8b20dcb105f4ee  use/config.sub
  -732bcd6b9e23f241e015d71b0a3a862104053aba20718c1f56b292cee7e29371  use/depcomp
  -608b76d735bb2ec2bcb1271644c3d5e7a428fb8d2338e114e8a48ebf91ccfd23  use/install-sh
  -2304d53af1f63b76a11651efdd18578adf2bda1ffc2c257100cba374b55f284b  use/ltmain.sh
  -f038345dab184e538098d22a8edc423762da66a90ebe269f23bfef85287cd30c  use/missing
  -7c1ae35455771ae32050c2ed109e3d297160b6d1a2f70b0278cf6968e5e7e98c  use/test-driver
  ```

  ---

  **UPDATE 2025-09-30**

  A few commits have been added:

  1. The first commit fixes the `$(package)_fetched` target, which erroneously succeeds on the second run after a failure on the master branch:
  ```
  $ gmake -C depends clean-all
  $ gmake -C depends qrencode_fetched FALLBACK_DOWNLOAD_PATH="https://fallback.invalid"
  $ gmake -C depends qrencode_fetched FALLBACK_DOWNLOAD_PATH="https://fallback.invalid"
  $ echo $?
  0
  ```
  With the first commit applied:
  ```
  $ gmake -C depends clean-all
  $ gmake -C depends qrencode_fetched FALLBACK_DOWNLOAD_PATH="https://fallback.invalid"
  $ gmake -C depends qrencode_fetched FALLBACK_DOWNLOAD_PATH="https://fallback.invalid"
  $ echo $?
  2
  ```

  2. The second and third commits allow the depends build subsystem to detect when the source tarball content has been modified and needs to be re-downloaded, even if the file name remains the same.

ACKs for top commit:
  m3dwards:
    ACK 93a70a42d3
  achow101:
    ACK 93a70a42d3
  vasild:
    ACK 93a70a42d3
  janb84:
    ACK 93a70a42d3

Tree-SHA512: 38b7c029070426196c747fc45c9d00bae534eeeb2d9cd9f221580fce8380f4f8aecb6c48b2563e322edd8c9534f5dd42d8f4e110ada42bb83568cf2dcfb7dc22
2025-10-07 16:57:58 -07:00
Hennadii Stepanov ec5841888d
Merge bitcoin/bitcoin#32513: ci: remove 3rd party js from windows dll gha job
156927903d ci: Check windows manifests for all executables (Max Edwards)
e1a1b14c93 ci: use a more generic way of finding mt.exe (Max Edwards)
7ae0497eef ci: remove 3rd party js from windows dll gha job (Max Edwards)

Pull request description:

  The windows job uses the external dependency `ilammy/msvc-dev-cmd` which runs javascript. We use this to put various tools on the path such as `MSBuild.exe` and `mt.exe`. We can remove this dependency and use `vswhere.exe` directly to find these tools and create a "[Developer command prompt](https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-powershell)" as someone would on their dev machine.

  While in this area of the code, this PR also runs some additional manifest checks on the windows binaries.

  Fixes: #32508

ACKs for top commit:
  davidgumberg:
    crACK 156927903d
  hebasto:
    ACK 156927903d.

Tree-SHA512: df640dff27579a1c95daddc5a5ba8fd655bbd0a6f2aff74d0f63439c7185c0b18a90abfee3f1f032fe833cd19b822ef71812f44b24c4c044222e46d01c271864
2025-10-08 00:44:05 +01:00
Ava Chow d735e2e9b3
Merge bitcoin/bitcoin#32998: Bump SCRIPT_VERIFY flags to 64 bit
652424ad16 test: additional test coverage for script_verify_flags (Anthony Towns)
417437eb01 script/verify_flags: extend script_verify_flags to 64 bits (Anthony Towns)
3cbbcb66ef script/interpreter: make script_verify_flag_name an ordinary enum (Anthony Towns)
bddcadee82 script/verify_flags: make script_verify_flags type safe (Anthony Towns)
a5ead122fe script/interpreter: introduce script_verify_flags typename (Anthony Towns)
4577fb2b1e rpc: have getdeploymentinfo report script verify flags (Anthony Towns)
a3986935f0 validation: export GetBlockScriptFlags() (Anthony Towns)
5db8cd2d37 Move mapFlagNames and FormatScriptFlags logic to script/interpreter.h (Anthony Towns)

Pull request description:

  We currently use 21 of 32 possible bits for `SCRIPT_VERIFY_*` flags, with open PRs that may use 8 more (#29247, #31989, #32247, #32453). The mutinynet fork that has included many experimental soft fork features is [already reusing bits here](d4a86277ed/src/script/interpreter.h (L175-L195)). Therefore, bump this to 64 bits.

  In order to make it easier to update this logic in future, this PR also introduces a dedicated type for the script flags, and disables implicit conversion between that type and the underlying integer type. To make verifying that this change doesn't cause flags to disappear, this PR also resurrects the changes from #28806 so that the script flags that are consensus enforced on each block can be queried via getdeploymentinfo.

ACKs for top commit:
  instagibbs:
    reACK 652424ad16
  achow101:
    ACK 652424ad16
  darosior:
    ACK 652424ad16
  theStack:
    Code-review ACK 652424ad16 🎏

Tree-SHA512: 7b30152196cdfdef8b9700b571b7d7d4e94d28fbc5c26ea7532788037efc02e4b1d8de392b0b20507badfdc26f5c125f8356a479604a9149b8aae23a7cf5549f
2025-10-07 14:51:22 -07:00
Ava Chow de1dc6b47b
Merge bitcoin/bitcoin#33515: Improve LastCommonAncestor performance + add tests
3635d62f5a chain: make use of pskip in LastCommonAncestor (optimization) (Pieter Wuille)
2e09d66fbb tests: add unit tests for CBlockIndex::GetAncestor and LastCommonAncestor (Pieter Wuille)

Pull request description:

  In theory, the `LastCommonAncestor` function in chain.cpp can take $\mathcal{O}(n)$ time, walking over the entire chain, if the forking point is very early, which could take ~milliseconds. I expect this to be very rare in normal occurrences, but it seems nontrivial to reason about worst cases as it's accessible from several places in net_processing.

  This PR modifies the algorithm to make use of the `CBlockIndex::pskip` skip pointers to find the forking point in sublinear time (a simulation shows that for heights up to $34 \cdot 4^k - 2$ and $k \geq 8$, no more than $k^2 + 10k + 13$ steps are ever needed), in a way that should be nearly free - at worst the same number of memory accesses should be made, with a tiny increase in computation.

  As it appears we didn't really have tests for this function, unit tests are added for that function as well as `CBlockIndex::GetAncestor()`.

  This is inspired by https://github.com/bitcoin/bitcoin/pull/32180#discussion_r2394877881

ACKs for top commit:
  optout21:
    ACK 3635d62f5a
  achow101:
    ACK 3635d62f5a
  vasild:
    ACK 3635d62f5a
  mzumsande:
    Code Review ACK 3635d62f5a
  furszy:
    ACK 3635d62f5a
  stratospher:
    ACK 3635d62f5a.

Tree-SHA512: f9b7dea1e34c1cc1ec1da3fb9e90c4acbf4aaf0f04768844f538201efa6b11eeeefc97b720509e78c21878977192e2c4031fd8974151667e2e756247002b8164
2025-10-07 13:54:25 -07:00
merge-script 919e6d01e9
Merge bitcoin/bitcoin#33489: build: Drop support for EOL macOS 13
1aaaaa078b fuzz: Drop unused workaround after Apple-Clang bump (MarcoFalke)
fadad7a494 Drop support for EOL macOS 13 (MarcoFalke)

Pull request description:

  Now that macOS 13 is EOL (https://en.wikipedia.org/wiki/MacOS_Ventura), it seems odd to still support it.

  (macOS Ventura 13.7.8 received its final security update on 20 Aug 2025: https://support.apple.com/en-us/100100)

  This patch will only be released in version 31.x, another 6 months out from now.

  So:

  * Update the depends build and release note template to drop EOL macOS 13.
  * As a result, update the earliest Xcode to version 16 in CI.
  * Also, bump the macOS CI runner to version 15, to avoid issues when version 14 will be at its EOL in about 1 year.

  This also allows to drop a small workaround in the fuzz tests and unlocks libcpp hardening (https://github.com/bitcoin/bitcoin/pull/33462)

ACKs for top commit:
  stickies-v:
    re-ACK 1aaaaa078b
  l0rinc:
    code review ACK 1aaaaa078b
  hodlinator:
    re-ACK 1aaaaa078b
  hebasto:
    ACK 1aaaaa078b.

Tree-SHA512: 6d247a8432ef8ea8c6ff2a221472b278f8344346b172980299507f9898bb9e8e16480c128b1f4ca692bcbcc393da2b2fd6895ac5f118bc09e0f30f910529d20c
2025-10-06 12:48:00 -04:00
merge-script 452ea59281
Merge bitcoin/bitcoin#33454: net: support overriding the proxy selection in ConnectNode()
c76de2eea1 net: support overriding the proxy selection in ConnectNode() (Vasil Dimov)

Pull request description:

  Normally `ConnectNode()` would choose whether to use a proxy and which one. Make it possible to override this from the callers and same for `OpenNetworkConnection()` - pass down the proxy to `ConnectNode()`.

  Document both functions.

  This is useful if we want to open connections to IPv4 or IPv6 peers through the Tor SOCKS5 proxy.

  Also have `OpenNetworkConnection()` return whether the connection succeeded or not. This can be used when the caller needs to keep track of how many (successful) connections were opened.

  ---

  This is part of [#29415 Broadcast own transactions only via short-lived Tor or I2P connections](https://github.com/bitcoin/bitcoin/pull/29415). Putting it in its own PR to reduce the size of #29415 and because it does not depend on the other commits from there.

ACKs for top commit:
  stratospher:
    ACK c76de2e.
  optout21:
    ACK c76de2eea1
  mzumsande:
    Code Review ACK c76de2eea1
  andrewtoth:
    ACK c76de2eea1

Tree-SHA512: 1d266e4280cdb1d0599971fa8b5da58b1b7451635be46abb15c0b823a1e18cf6e7bcba4a365ad198e6fd1afee4097d81a54253fa680c8b386ca6b9d68d795ff0
2025-10-06 12:43:14 -04:00
merge-script a33bd767a3
Merge bitcoin/bitcoin#33464: p2p: Use network-dependent timers for inbound inv scheduling
0f7d4ee4e8 p2p: Use different inbound inv timer per network (Martin Zumsande)
94db966a3b net: use generic network key for addrcache (Martin Zumsande)

Pull request description:

  Currently, `NextInvToInbounds` schedules  each round of `inv` at the same time for all inbound peers. It's being done this way because with a separate timer per peer (like it's done for outbounds), an attacker could do multiple connections to learn about the time a transaction arrived. (#13298).

  However, having a single timer for inbounds of all networks is also an obvious fingerprinting vector: Connecting to a suspected pair of privacy-network and clearnet addresses and observing the `inv` pattern makes it trivial to confirm or refute that they are the same node.

  This PR changes it such that a separate timer is used for each network.
  It uses the existing method  from `getaddr` caching and generalizes it to be saved in a new field `m_network_key` in `CNode` which will be used for both `getaddr` caching and `inv` scheduling, and can also be used for any future anti-fingerprinting measures.

ACKs for top commit:
  sipa:
    utACK 0f7d4ee4e8
  stratospher:
    reACK 0f7d4ee.
  naiyoma:
    Tested ACK 0f7d4ee4e8
  danielabrozzoni:
    reACK 0f7d4ee4e8

Tree-SHA512: e197c3005b2522051db432948874320b74c23e01e66988ee1ee11917dac0923f58c1252fa47da24e68b08d7a355d8e5e0a3ccdfa6e4324cb901f21dfa880cd9c
2025-10-03 23:45:17 +01:00
merge-script 2578da69f4
Merge bitcoin/bitcoin#33485: test: set par=2 in default config for functional test framework
dda5228e02 test: set par=2 in default config for functional test framework (Andrew Toth)

Pull request description:

  Depending on the host machine, a default `par` value can spawn up to 15 script verification threads for each node. Running the functional test suite with default `par` can exhaust file descriptors or hit other resource limits when many threads are spawned. These threads are mostly idle and the same code paths are executed with a value of `par=2`. Limit this to 2 for functional tests that do not override the default option.

ACKs for top commit:
  maflcko:
    lgtm ACK dda5228e02
  pablomartin4btc:
    ACK dda5228e02
  l0rinc:
    Code review ACK dda5228e02
  theStack:
    ACK dda5228e02

Tree-SHA512: 4459972330ff50ac7391141db6382579de09d84e68959eaeb5f20972bb9daf9aac1bd68355028ded9ee65e838c12dbd53e6f3bb6cdc375d269f666c19a19eaec
2025-10-03 22:36:34 +01:00
merge-script 25dbe4bc86
Merge bitcoin/bitcoin#33533: test: addrman: check isTerrible when time is more than 10min in the future
8e47ed6906 test: addrman: check isTerrible when time is more than 10min in the future (brunoerg)

Pull request description:

  This PR adds test coverage to kill the following mutant (https://corecheck.dev/mutation/src/addrman.cpp#L76):
  ```diff
  diff --git a/src/addrman.cpp b/src/addrman.cpp
  index 9c3a24db90..0ffd349315 100644
  --- a/src/addrman.cpp
  +++ b/src/addrman.cpp
  @@ -73,7 +73,7 @@ bool AddrInfo::IsTerrible(NodeSeconds now) const
       }

       if (nTime > now + 10min) { // came in a flying DeLorean
  -        return true;
  +        return false;
       }
  ```

  When the `nTime` is set 10 minutes in the future the addr should be marked as terrible.

ACKs for top commit:
  Crypt-iQ:
    crACK 8e47ed6906
  danielabrozzoni:
    tACK 8e47ed6906
  marcofleon:
    Nice, code review ACK 8e47ed6906

Tree-SHA512: b53b3aa234a73ec7808cb1555916ac64dd707f230ec290a1712493ece8e274a060e16d862b31df0f744804ebd3c0c2825c49becb7d3040cc358e48c4002524cb
2025-10-03 20:20:46 +01:00
merge-script cfb0d74698
Merge bitcoin/bitcoin#33121: test: fix p2p_leak_tx.py
14ae71f323 test: make notfound_on_unannounced more reliable (David Gumberg)
99bc552980 test: fix (w)txid confusion in p2p_leak_tx.py (Martin Zumsande)
576dd97cb9 test: increase timeout in p2p_leak_tx.py (Martin Zumsande)

Pull request description:

  This fixes two issues with `p2p_leak_tx.py`:

  1.) #33090: As far as I can see, this is just the randomness of `NextInvToInbounds`/ `rand_exp_duration`, which has a probability of `e^-(60s/5s) = 6.14×10^−6` to result in a period > 60s (our waiting time), so that the test would fail every 160k runs... Doubling the timeout should be sufficient to lower the probability drastically.

  2.) The subtest `test_notfound_on_unannounced_tx` has some (w)txid confusion: we send a `MSG_TX`-type getdata with a `wtxid` in it, which necessarily always results in a NOTFOUND. Fixed this, and change the subtest to be more deterministic based on `mocktime`.

ACKs for top commit:
  stratospher:
    ACK 14ae71f. nice restructuring using mocktime!
  davidgumberg:
    reACK 14ae71f323
  vasild:
    ACK 14ae71f323

Tree-SHA512: be5a4ca7bf56f82b6fa04d90ef9312dc2e6f8ff7ddf70b39d979dc42fbdd823157109b8b5dc46eb7f81ac1e816f40e6966b3c8a7d384aadee01e2189c20d3e3a
2025-10-03 20:06:50 +01:00
merge-script 86eaa4d6cd
Merge bitcoin/bitcoin#33482: contrib: fix macOS deployment with no translations
7b5261f7ef contrib: fix using macdploy script without translations. (amisha)

Pull request description:

  **Description**
  From what I deciphered reading the line https://github.com/bitcoin/bitcoin/blob/master/contrib/macdeploy/macdeployqtplus#L390 is that qt translations are optional to have hence we should be able to build without it but the case where the flag translations_dir falls back to its default Null value it raises this error.

  The config comments also mentioned that adding translation file is optional.

  ```
  ./macdeployqtplus --help
  usage: macdeployqtplus [-h] [-verbose [VERBOSE]] [-no-plugins] [-no-strip] [-translations-dir path] [-zip zip] app-bundle

  Improved version of macdeployqt. Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .zip file. Note, that the "dist" folder will be deleted before deploying on each run. Optionally, Qt translation files
  (.qm) can be added to the bundle.
  ```

  **Steps to reproduce**
  So I was following the general steps to set up app on macos however I didn't download any qt translations presuming it was optional from the comment linkedin in PR, so to reproduce if you have translation directories in place ull need to delete them and then try to build the file, otherwise don't download it at all and try to build it. It should fail on that flag as translations dir was never downloaded.

  **Approach taken**
  I have moved the code which adds language files under the if statement that first checks if the value of the flag is not Null before referencing it.

ACKs for top commit:
  ismaelsadeeq:
    ACK 7b5261f7ef

Tree-SHA512: 8d51b17569e42c9feb95e1be17b1551c708a05eb44b82c74db0b25e07006b4ee223d64484f8bdb2ee1420f6e571686561ae1c09bd3362f77dcbb507bc5085f86
2025-10-03 15:40:38 +01:00
merge-script 007900ee9b
Merge bitcoin/bitcoin#33434: depends: static libxcb-cursor
eca50854e1 depends: static libxcb_cursor (fanquake)

Pull request description:

  Remove the runtime requirement of `libxcb-cursor`. This library is no-longer present on modern Ubuntu.
  Fixes #33432.
  Also related to #32097.

ACKs for top commit:
  davidgumberg:
    Addendum ACK eca50854e1
  willcl-ark:
    Code review ACK eca50854e1

Tree-SHA512: d545a03baf5030de64874b79add87b6ef5f95eb5ca31aa66007ee03554103d2eda5e56dfd4395d0a12e24b2e489457e4f19ed9e6d390351c72a0da630f03cc42
2025-10-03 15:26:20 +01:00
brunoerg 8e47ed6906 test: addrman: check isTerrible when time is more than 10min in the future 2025-10-03 10:24:29 -03:00
Pieter Wuille 3635d62f5a chain: make use of pskip in LastCommonAncestor (optimization)
By using the pskip pointer, which regularly allows jumping back much faster
than pprev, the forking point between two CBlockIndex entries can be found
much faster.

A simulation shows that no more than 136 steps are needed to jump anywhere
within the first 2^20 block heights, and on average 65 jumps for uniform
forking points around that height.
2025-10-02 10:34:12 -04:00
Pieter Wuille 2e09d66fbb tests: add unit tests for CBlockIndex::GetAncestor and LastCommonAncestor 2025-10-02 10:34:09 -04:00
Max Edwards 156927903d ci: Check windows manifests for all executables
The other executables have manifests and these should be checked in
addition to bitcoind. Skipping fuzz.exe, bench_bitcoin.exe and
test_bitcoin-qt.exe as they do not have manifests.
2025-10-02 15:13:29 +01:00
Max Edwards e1a1b14c93 ci: use a more generic way of finding mt.exe
This sets up a vs developer command prompt and should hopefully should
be more resilient to upstream changes

Co-authored-by: David Gumberg <davidzgumberg@gmail.com>
2025-10-02 15:13:26 +01:00
merge-script 1ed00a0d39
Merge bitcoin/bitcoin#33504: Mempool: Do not enforce TRUC checks on reorg
06df14ba75 test: add more TRUC reorg coverge (Greg Sanders)
26e71c237d Mempool: Do not enforce TRUC checks on reorg (Greg Sanders)
bbe8e9063c fuzz: don't bypass_limits for most mempool harnesses (Greg Sanders)

Pull request description:

  This was the intended behavior but our tests didn't cover the scenario where in-block transactions themselves violate TRUC topological constraints.

  The behavior in master will potentially lead to many erroneous evictions during a reorg, where evicted TRUC packages may be very high feerate and make sense to mine all together in the next block and are well within the normal anti-DoS chain limits.

  This issue exists since the merge of https://github.com/bitcoin/bitcoin/pull/28948/files#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R956

ACKs for top commit:
  sdaftuar:
    ACK 06df14ba75
  glozow:
    ACK 06df14ba75
  ismaelsadeeq:
    Code review ACK 06df14ba75

Tree-SHA512: bdb6e4dd622ed8b0b11866263fff559fcca6e0ca1c56a884cca9ac4572f0026528a63a9f4c8a0660df2f5efe0766310a30e5df1d6c560f31e4324ea5d4b3c1a8
2025-10-02 13:22:22 +01:00
Vasil Dimov c76de2eea1
net: support overriding the proxy selection in ConnectNode()
Normally `ConnectNode()` would choose whether to use a proxy and which
one. Make it possible to override this from the callers and same for
`OpenNetworkConnection()` - pass down the proxy to `ConnectNode()`.

Document both functions.

This is useful if we want to open connections to IPv4 or IPv6 peers
through the Tor SOCKS5 proxy.

Also have `OpenNetworkConnection()` return whether the connection
succeeded or not. This can be used when the caller needs to keep track
of how many (successful) connections were opened.
2025-10-02 08:39:26 +02:00
Ava Chow 75353a0163
Merge bitcoin/bitcoin#32326: net: improve the interface around FindNode() and avoid a recursive mutex lock
87e7f37918 doc: clarify peer address in getpeerinfo and addnode RPC help (Vasil Dimov)
2a4450ccbb net: change FindNode() to not return a node and rename it (Vasil Dimov)
4268abae1a net: avoid recursive m_nodes_mutex lock in DisconnectNode() (Vasil Dimov)
3a4d1a25cf net: merge AlreadyConnectedToAddress() and FindNode(CNetAddr) (Vasil Dimov)

Pull request description:

  `CConnman::FindNode()` would lock `m_nodes_mutex`, find the node in `m_nodes`, release the mutex and return the node. The current code is safe but it is a dangerous interface where a caller may end up using the node returned from `FindNode()` without owning `m_nodes_mutex` and without having that node's reference count incremented.

  Change `FindNode()` to return a boolean since all but one of its callers used its return value to check whether a node exists and did not do anything else with the return value.

  Remove a recursive lock on `m_nodes_mutex`.

  Rename `FindNode()` to better describe what it does.

ACKs for top commit:
  achow101:
    ACK 87e7f37918
  furszy:
    Code review ACK 87e7f37918
  hodlinator:
    re-ACK 87e7f37918

Tree-SHA512: 44fb64cd1226eca124ed1f447b4a1ebc42cc5c9e8561fc91949bbeaeaa7fa16fcfd664e85ce142e5abe62cb64197c178ca4ca93b3b3217b913e3c498d0b7d1c9
2025-10-01 14:17:22 -07:00
Vasil Dimov 87e7f37918
doc: clarify peer address in getpeerinfo and addnode RPC help
The returned value in `getpeerinfo/addr` could be a hostname as well as
an IP address and the `:port` part could be missing. It is displayed
from `CNode::m_addr_name` which could have been set from RPC `addnode`
where the argument is allowed to be a hostname and an optional port.
2025-10-01 16:39:56 +02:00
Vasil Dimov 2a4450ccbb
net: change FindNode() to not return a node and rename it
All callers of `CConnman::FindNode()` use its return value `CNode*` only
as a boolean null/notnull. So change that method to return `bool`.

This removes the dangerous pattern of handling a `CNode` object (the
return value of `FindNode()`) without holding `CConnman::m_nodes_mutex`
and without having that object's reference count incremented for the
duration of the usage.

Also rename the method to better describe what it does.
2025-10-01 16:39:56 +02:00
Vasil Dimov 4268abae1a
net: avoid recursive m_nodes_mutex lock in DisconnectNode()
Have `CConnman::DisconnectNode()` iterate `m_nodes` itself instead of
using `FindNode()`. This avoids recursive mutex lock and drops the only
caller of `FindNode()` which used the return value for something else
than a boolean found/notfound.
2025-10-01 16:39:55 +02:00
Hennadii Stepanov acc7f2a433
Merge bitcoin/bitcoin#33401: ci: Remove bash -c from cmake invocation using eval
50194029e7 ci: Remove bash -c from cmake invocation using eval (Brandon Odiwuor)

Pull request description:

  Follow up to https://github.com/bitcoin/bitcoin/pull/32970

  https://github.com/bitcoin/bitcoin/pull/32970#r2213730157

  > Does `cmake -S ...` still need to be wrapped in `bash -c "..."`?

  https://github.com/bitcoin/bitcoin/pull/32970#r2213741192
  > It is not trivial to replace. Maybe the `eval` hack from below can be used:
  >
  > ```shell
  >   # parses TEST_RUNNER_EXTRA as an array which allows for multiple arguments such as TEST_RUNNER_EXTRA='--exclude "rpc_bind.py --ipv6"'
  >
  >   eval "TEST_RUNNER_EXTRA=($TEST_RUNNER_EXTRA)"
  > ```
  >however, I haven't tried this yet.

  https://github.com/bitcoin/bitcoin/pull/32970#r2213801696
  > Yeah, the eval hack should work:
  >
  > ```
  > $ export T="-DREDUCE_EXPORTS=ON -DCMAKE_CXX_FLAGS='-Wno-psabi     -Wno-error=maybe-uninitialized'";  eval "T=($T)"; for i in "${T[@]}"; do  echo "_${i}_" ; done
  > _-DREDUCE_EXPORTS=ON_
  > _-DCMAKE_CXX_FLAGS=-Wno-psabi     -Wno-error=maybe-uninitialized_
  > ```
  >
  > (can be done in a follow-up)

  This replaces the `bash -c` wrapper with an eval-based array parsing to preserve spaces in flag values (e.g., in CMAKE_CXX_FLAGS), allowing ShellCheck to lint the cmake command

ACKs for top commit:
  maflcko:
    lgtm ACK 50194029e7
  hebasto:
    ACK 50194029e7.

Tree-SHA512: 6fd22569e2c719a8d13805f18e1e7e3b8eb57d0a6307f2e7175988b25750eafb7c8260796c8e7350db67d622dbe97e6af7bab8ee52187bb8e8eeae3740a47c01
2025-10-01 11:36:10 +01:00
MarcoFalke 1aaaaa078b
fuzz: Drop unused workaround after Apple-Clang bump 2025-10-01 08:09:34 +02:00
MarcoFalke fadad7a494
Drop support for EOL macOS 13 2025-10-01 08:09:30 +02:00
Brandon Odiwuor 50194029e7 ci: Remove bash -c from cmake invocation using eval 2025-10-01 08:58:17 +03:00
Ava Chow f41f97240c
Merge bitcoin/bitcoin#28584: Fuzz: extend CConnman tests
0802398e74 fuzz: make it possible to mock (fuzz) CThreadInterrupt (Vasil Dimov)
6d9e5d130d fuzz: add CConnman::SocketHandler() to the tests (Vasil Dimov)
3265df63a4 fuzz: add CConnman::InitBinds() to the tests (Vasil Dimov)
91cbf4dbd8 fuzz: add CConnman::CreateNodeFromAcceptedSocket() to the tests (Vasil Dimov)
50da7432ec fuzz: add CConnman::OpenNetworkConnection() to the tests (Vasil Dimov)
e6a917c8f8 fuzz: add Fuzzed NetEventsInterface and use it in connman tests (Vasil Dimov)
e883b37768 fuzz: set the output argument of FuzzedSock::Accept() (Vasil Dimov)

Pull request description:

  Extend `CConnman` fuzz tests to also exercise the methods `OpenNetworkConnection()`, `CreateNodeFromAcceptedSocket()`, `InitBinds()` and `SocketHandler()`.

  Previously fuzzing those methods would have resulted in real socket functions being called in the operating system which is undesirable during fuzzing. Now that https://github.com/bitcoin/bitcoin/pull/21878 is complete all those are mocked to a fuzzed socket and a fuzzed DNS resolver (see how `CreateSock` and `g_dns_lookup` are replaced in the first commit).

ACKs for top commit:
  achow101:
    ACK 0802398e74
  jonatack:
    Review re-ACK 0802398e74
  dergoegge:
    Code review ACK 0802398e74

Tree-SHA512: a717d4e79f42bacf2b029c821fdc265e10e4e5c41af77cd4cb452cc5720ec83c62789d5b3dfafd39a22cc8c0500b18169aa7864d497dded729a32ab863dd6c4d
2025-09-30 15:59:09 -07:00
Ava Chow cc4a2cc6bd
Merge bitcoin/bitcoin#33453: docs: Undeprecate datacarrier and datacarriersize configuration options
451ba9ada4 datacarrier: Undeprecate configuration option (Anthony Towns)

Pull request description:

  Removes the deprecation for the `datacarrier` and `datacarriersize` options by reverting commit 0b4048c733 from https://github.com/bitcoin/bitcoin/pull/32406

  **Many current Bitcoin Core users want to continue using this option**
  This statement is based on public postings from many Bitcoin Core users and not a formal survey. AJ Towns’ observation from [#32406](0b4048c733 (r2084024874)) that “_for now there seem to be a bunch of users who like the option_” has only become more apparent in the months since.

  **The deprecation intent is unclear to users**
  This echo’s Ava Chow’s comment from #32714 that “_IMO we should not have removal warnings if there is no current plan to actually remove them._” In months since that comment, partially due to increased feedback from Bitcoin Core users wanting to keep this option, there is even less likelihood of a near term plan to remove these options. That leaves Bitcoin Core users in an unclear situation: the option could be removed in the next version or perhaps never. Removing the deprecation gives clarity for their planning purposes. Deprecating the option in the future, preferably with a removal schedule to better inform users, would still be possible.

  **Minimal downsides to removing deprecation**
  As a best practice, Bitcoin Core has avoided an option when the developers cannot articulate when they should be used. There is non-zero maintenance cost to keeping this code around (although leaving the options deprecated for a long time has the same effect). “Don’t offer users footguns” is also a good principle, but with this option, there seems to be only small impacts that can quickly be remedied by changing the option value by Bitcoin Core users. There already exist in Bitcoin Core more potentially-user-harmful options/values than what datacarrier might cause.

ACKs for top commit:
  ajtowns:
    ACK 451ba9ada4
  darosior:
    That said, certain users care strongly about using those options. In these conditions, i do not see the project removing the option anytime soon. Therefore i think it's technically incorrect (and confusing) to mark it as deprecated. utACK 451ba9ada4 on removing the deprecation.
  instagibbs:
    crACK 451ba9ada4
  Raimo33:
    ACK 451ba9ada4
  Ademan:
    utACK 451ba9a
  ryanofsky:
    Code review ACK 451ba9ada4
  marcofleon:
    ACK 451ba9ada4
  achow101:
    ACK 451ba9ada4
  moonsettler:
    ACK 451ba9ada4
  ismaelsadeeq:
    utACK 451ba9ada4 🛰️
  jonatack:
    ACK 451ba9ada4
  Zero-1729:
    crACK 451ba9ada4
  vasild:
    ACK 451ba9ada4

Tree-SHA512: b83fc509f5dd820976596e1ae9fb69a22ada567e0e0ac88da5fc5e940a46d8894b40cc70c3eff2cbdabd4da5ec913f0d18c1632fc906f210b308855868410699
2025-09-30 15:23:20 -07:00
Ava Chow 7502d4e940
Merge bitcoin/bitcoin#33260: test: Use extra_port() helper in feature_bind_extra.py
fabc2615af test: Use extra_port() helper in feature_bind_extra.py (MarcoFalke)

Pull request description:

  This is a refactor for self-validating and self-documenting code.

  Currently, the test assumes that extra ports are available and just increments them without checking. However, this may not be the case when the test is modified to use more ports. In this case, the tests may fail intermittently and the failure is hard to debug.

  Fix this confusion, by calling `p2p_port` each time. This ensures the required `assert n <= MAX_NODES` is checked each time.

  Closes https://github.com/bitcoin/bitcoin/issues/33250

ACKs for top commit:
  achow101:
    ACK fabc2615af
  janb84:
    crACK fabc2615af
  w0xlt:
    ACK fabc2615af

Tree-SHA512: 1eff00be7f43104ae8a66e79fbf64075ec22bb20f392ac1e4c8a7dd694d4f1760aa44ea54ab7b1f2b947ab018851ab3c10d3c717714c0bee4d8d24617594c2bb
2025-09-30 13:30:36 -07:00
David Gumberg 14ae71f323 test: make notfound_on_unannounced more reliable
By using mocktime, we will always hit both the notfound
branch and the tx sent branch.
The previous version didn't achieve that due to timing
issues.

Co-authored-by: Martin Zumsande <mzumsande@gmail.com>
2025-09-30 15:57:31 -04:00
Martin Zumsande 99bc552980 test: fix (w)txid confusion in p2p_leak_tx.py
Before, we'd send a MSG_TX with a wtxid in it, which
would always result in a notfound answer
2025-09-30 15:57:31 -04:00
Martin Zumsande 576dd97cb9 test: increase timeout in p2p_leak_tx.py
With a low but not negligible probability in the order
of 10^-6 the exponential timer NextInvToInBounds can lead
to an interval >60s, making the test fail.
Also uses mocktime to speed up the test and fixes a
non-matching on_inv override.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2025-09-30 15:56:17 -04:00
Ava Chow 8f73d95221
Merge bitcoin/bitcoin#33299: wallet: reduce unconditional logging during load
fc861332b3 wallet, log: reduce unconditional logging during load (furszy)

Pull request description:

  Currently the unconditional log during init with a default wallet happens three times:
  ```
  2025-09-03T19:57:16Z init message: Verifying wallet(s)…
  2025-09-03T19:57:16Z Using SQLite Version 3.45.1
  2025-09-03T19:57:16Z Using wallet XXX/.bitcoin/regtest
  2025-09-03T19:57:16Z Using SQLite Version 3.45.1
  2025-09-03T19:57:16Z Using wallet XXX/.bitcoin/regtest
  (...)
  2025-09-03T19:57:16Z Using SQLite Version 3.45.1
  2025-09-03T19:57:16Z Using wallet XXX/.bitcoin/regtest
  2025-09-03T19:57:16Z init message: Loading wallet…
  ```
  For non-default wallets it's logged two times.

  That seems a bit too much, so just log the SQLite version just one, and remove the log for the full path of the wallet, since it's already clear from other logs which wallet is being loaded.

ACKs for top commit:
  achow101:
    ACK fc861332b3
  furszy:
    utACK fc861332b3
  stickies-v:
    ACK fc861332b3

Tree-SHA512: ca45c8ede985e6feab0cb93d718a6d633691276ca6e5f13f6471759f11dee98b312e1c802a7fb42c7fa859b6edc44a8c54b9e2ca389655cf028aebf2dabe51f6
2025-09-30 11:01:27 -07:00
Martin Zumsande 0f7d4ee4e8 p2p: Use different inbound inv timer per network
Currently nodes schedule their invs to all inbound peers at the same time.
It is trivial to make use this timing pattern for fingerprinting
identities on different networks. Using a separate timers for each network will
make the fingerprinting harder.
2025-09-30 11:17:17 -04:00
Hennadii Stepanov 93a70a42d3
depends: Update URL for `qrencode` package source tarball
The https://fukuchi.org/ homepage no longer links to the source tarball,
and previously available files appear to have been removed. The homepage
now instructs users to download source tarballs from the GitHub releases
page instead.

The diff between the source trees is immaterial.
2025-09-30 11:26:47 +01:00
Hennadii Stepanov 6de8051263
depends: Use hash instead of file name for package download stamp
The package version is still included for convenience.
2025-09-30 11:26:29 +01:00
Hennadii Stepanov 46135d90ea
depends: Drop redundant check for downloaded file
The `fetch_file` commands are invoked for the `$($(package)_fetched)`
target, so the existence of the download stamp has already been tested.
2025-09-30 11:22:50 +01:00
Hennadii Stepanov 771978952a
depends: Fix `$(package)_fetched` target
Ensure the download timestamp is created only after a successful
download.
2025-09-30 11:20:12 +01:00
Hennadii Stepanov 25212dfdb4
Merge bitcoin/bitcoin#33487: ci: use latest versions of lint deps
d4f47f9771 ci: use latest versions of lint deps (fanquake)

Pull request description:

  Some of the versions used here are > 2 years old. i.e `mypy`. Use the latest avilable versions, except for LIEF, which is generally changed with Guix.

  Side note. I can't remember the last time one of these tools (mypy, ruff, vulture) actually caught an issue in the lint job.

ACKs for top commit:
  maflcko:
    lgtm ACK d4f47f9771
  janb84:
    lgtm ACK d4f47f9771
  hebasto:
    ACK d4f47f9771, I have reviewed the code and it looks OK.

Tree-SHA512: 8b312535c9fea8e76d58f517ada6d6ea7a119c5e03c8cb84a41b5b6ca80dfaaff65a81478bdc1a5acf734cfb0bc66a8b3ba5400db8973c43ca913b07568abfe4
2025-09-30 08:17:14 +01:00
Greg Sanders 06df14ba75 test: add more TRUC reorg coverge 2025-09-29 16:25:54 -04:00
Greg Sanders 26e71c237d Mempool: Do not enforce TRUC checks on reorg
Not enforcing TRUC topology on reorg was the intended
behavior, but the appropriate bypass argument was not
checked.

This mistake means we could potentially invalidate a long
chain of perfectly incentive-compatible transactions that
were made historically, including subsequent non-TRUC
transactions, all of which may have been very high feerate.

Lastly, it wastes CPU cycles doing topology checks since
this behavior cannot actually enforce the topology in
general for the reorg setting.
2025-09-29 16:25:54 -04:00
Greg Sanders bbe8e9063c fuzz: don't bypass_limits for most mempool harnesses
Using bypass_limits=true is essentially fuzzing part of a
reorg only, and results in TRUC invariants unable to be
checked. Remove most instances of bypassing limits, leaving
one harness able to do so.
2025-09-29 16:25:54 -04:00
fanquake d4f47f9771
ci: use latest versions of lint deps
Some of the versions used here are > 2 years old. i.e mypy. Use the
latest avilable versions, except for LIEF, which is generally changed
with Guix.
2025-09-29 14:47:41 -04:00
furszy fc861332b3 wallet, log: reduce unconditional logging during load
The removed statements were logged up to two or three times for each loaded
wallet. The SQLite version only needs to be logged once.
The full wallet path is dropped, since the existing unconditional
logging while loading wallets is sufficient (also reduces anonymization
efforts in case of sharing logs).

Co-authored-by: Martin Zumsande <mzumsande@gmail.com>
2025-09-29 13:59:44 -04:00
Vasil Dimov 3a4d1a25cf
net: merge AlreadyConnectedToAddress() and FindNode(CNetAddr)
`CConnman::AlreadyConnectedToAddress()` is the only caller of
`CConnman::FindNode(CNetAddr)`, so merge the two in one function.

The unit test that checked whether `AlreadyConnectedToAddress()` ignores
the port is now unnecessary because now the function takes a `CNetAddr`
argument. It has no access to the port.
2025-09-29 12:51:52 +02:00
Andrew Toth dda5228e02 test: set par=2 in default config for functional test framework
Depending on the host machine, a default `par` value can spawn up to 15 script verification threads for each node.
Running the functional test suite with default `par` can exhaust file descriptors or hit other resource limits when many threads are spawned.
These threads are mostly idle and the same code paths are executed with a value of `par=2`.
Limit this to 2 for functional tests that do not override the default option.

Co-authored-by: maflcko <6399679+maflcko@users.noreply.github.com>
2025-09-27 16:31:01 -04:00
amisha 7b5261f7ef contrib: fix using macdploy script without translations.
QT translations are optional, but the script would error when
'translations_dir' falls back to its default value NULL.

This PR fixes it by moving the set-up of QT translations under
the check for 'translations_dir' presence.
2025-09-26 10:09:30 +05:30
Max Edwards 7ae0497eef ci: remove 3rd party js from windows dll gha job
We can use vswhere.exe directly to create a vs developer
prompt and so can remove this third party dependency.

Co-authored-by: David Gumberg <davidzgumberg@gmail.com>
2025-09-25 18:12:02 +01:00
Martin Zumsande 94db966a3b net: use generic network key for addrcache
The generic key can also be used in other places
where behavior between different network identities should
be uncorrelated to avoid fingerprinting.
This also changes RANDOMIZER_ID - since it is not
being persisted to disk, there are no compatibility issues.
2025-09-23 10:56:44 -04:00
fanquake eca50854e1
depends: static libxcb_cursor
Modern Ubuntu isn't shipping with this library installed by default.
Staticly link it to remove the need for end-users to install it.

Closes #33432.
2025-09-23 10:43:55 -04:00
Anthony Towns 451ba9ada4
datacarrier: Undeprecate configuration option
Reverts commit 0b4048c733
2025-09-22 02:47:02 -05:00
Anthony Towns 652424ad16 test: additional test coverage for script_verify_flags 2025-09-11 11:24:09 +10:00
MarcoFalke fabc2615af
test: Use extra_port() helper in feature_bind_extra.py 2025-08-27 09:46:26 +02:00
Anthony Towns 417437eb01 script/verify_flags: extend script_verify_flags to 64 bits 2025-08-14 10:17:32 +10:00
Anthony Towns 3cbbcb66ef script/interpreter: make script_verify_flag_name an ordinary enum
Instead of having `SCRIPT_VERIFY_FOO = (1U << n)` just have it
be `n` directly, and do the bit shifting when converting it to
`script_verify_flags`.
2025-08-14 10:17:32 +10:00
Anthony Towns bddcadee82 script/verify_flags: make script_verify_flags type safe
`using script_verify_flags = uint32_t` allows implicit conversion to
and from int, so replace it with a class to have the compiler ensure we
use the correct type. Provide from_int and as_int to allow for explicit
conversions when desired.

Introduces the type `script_verify_flag_name` for the individual flag
name enumeration.
2025-08-14 10:17:32 +10:00
Anthony Towns a5ead122fe script/interpreter: introduce script_verify_flags typename
Previously the SCRIPT_VERIFY_* flags were specified as either uint32_t,
unsigned int, or unsigned. This converts them to a common type alias in
preparation for changing the underlying type.
2025-08-14 10:17:32 +10:00
Anthony Towns 4577fb2b1e rpc: have getdeploymentinfo report script verify flags 2025-08-14 10:17:32 +10:00
Anthony Towns a3986935f0 validation: export GetBlockScriptFlags() 2025-08-14 10:17:32 +10:00
Anthony Towns 5db8cd2d37 Move mapFlagNames and FormatScriptFlags logic to script/interpreter.h
Moves FormatScriptFlags logic into GetScriptFlagNames which returns a
vector of strings. For completeness, also has GetScriptFlagNames report
on any bits that do not match a known script flag.
2025-08-14 10:17:30 +10:00
Vasil Dimov 0802398e74
fuzz: make it possible to mock (fuzz) CThreadInterrupt
* Make the methods of `CThreadInterrupt` virtual and store a pointer to
  it in `CConnman`, thus making it possible to override with a mocked
  instance.
* Initialize `CConnman::m_interrupt_net` from the constructor, making it
  possible for callers to supply mocked version.
* Introduce `FuzzedThreadInterrupt` and `ConsumeThreadInterrupt()` and
  use them in `src/test/fuzz/connman.cpp` and `src/test/fuzz/i2p.cpp`.

This improves the CPU utilization of the `connman` fuzz test.

As a nice side effect, the `std::shared_ptr` used for
`CConnman::m_interrupt_net` resolves the possible lifetime issues with
it (see the removed comment for that variable).
2025-06-09 14:17:33 +02:00
Vasil Dimov 6d9e5d130d
fuzz: add CConnman::SocketHandler() to the tests 2025-06-09 14:17:33 +02:00
Vasil Dimov 3265df63a4
fuzz: add CConnman::InitBinds() to the tests 2025-06-09 14:17:29 +02:00
Vasil Dimov 91cbf4dbd8
fuzz: add CConnman::CreateNodeFromAcceptedSocket() to the tests 2025-06-09 14:13:53 +02:00
Vasil Dimov 50da7432ec
fuzz: add CConnman::OpenNetworkConnection() to the tests
Now that all network calls done by `CConnman::OpenNetworkConnection()`
are done via `Sock` they can be redirected (mocked) to `FuzzedSocket`
for testing.
2025-06-09 14:13:52 +02:00
Vasil Dimov e6a917c8f8
fuzz: add Fuzzed NetEventsInterface and use it in connman tests 2025-06-09 14:13:49 +02:00
Vasil Dimov e883b37768
fuzz: set the output argument of FuzzedSock::Accept()
`FuzzedSock::Accept()` properly returns a new socket, but it forgot to
set the output argument `addr`, like `accept(2)` is expected to.

This could lead to reading uninitialized data during testing when we
read it, e.g. from `CService::SetSockAddr()` which reads the `sa_family`
member.

Set `addr` to a fuzzed IPv4 or IPv6 address.
2025-06-09 10:45:52 +02:00
80 changed files with 1095 additions and 492 deletions

View File

@ -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
@ -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'
@ -370,19 +387,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.

View File

@ -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" | \

View File

@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8
export CONTAINER_NAME="ci_mac_native_fuzz" # macos does not use a container, but the env var is needed for logging export CONTAINER_NAME="ci_mac_native_fuzz" # macos does not use a container, but the env var is needed for logging
export CMAKE_GENERATOR="Ninja" export CMAKE_GENERATOR="Ninja"
export BITCOIN_CONFIG="-DBUILD_FOR_FUZZING=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000'" export BITCOIN_CONFIG="-DBUILD_FOR_FUZZING=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000' -DAPPEND_CPPFLAGS='-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG'"
export CI_OS_NAME="macos" export CI_OS_NAME="macos"
export NO_DEPENDS=1 export NO_DEPENDS=1
export OSX_SDK="" export OSX_SDK=""

View File

@ -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")

View File

@ -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

View File

@ -460,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))
# ------------------------------------------------ # ------------------------------------------------

View File

@ -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 $$@

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -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.

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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.

View File

@ -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."));

View File

@ -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),
@ -1832,6 +1827,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
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},
@ -1841,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,
@ -2103,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.
@ -2120,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
@ -2216,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);
@ -2230,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();
@ -2255,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)
@ -2318,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) {
@ -2334,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);
} }
@ -2535,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();
} }
} }
@ -2564,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)};
@ -2580,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()) {
@ -2761,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();
@ -2864,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());
@ -2879,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);
} }
} }
} }
@ -2975,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);
@ -2995,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);
@ -3030,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;
@ -3083,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()) {
@ -3211,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);
@ -3312,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)
@ -3349,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;
{ {
@ -3425,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) {
@ -3519,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);
@ -3641,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;
@ -3804,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},
@ -3816,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},

View File

@ -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,18 +1396,49 @@ 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);
/**
* 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 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.

View File

@ -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
@ -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)
@ -5715,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);
} }

View File

@ -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};

View File

@ -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;
}, },

View File

@ -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), ", ") + ")"},
@ -322,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)"},
}, },

View File

@ -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;
}

View File

@ -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

71
src/script/verify_flags.h Normal file
View File

@ -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

View File

@ -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)
{ {

View File

@ -26,6 +26,7 @@ add_executable(test_bitcoin
bloom_tests.cpp bloom_tests.cpp
bswap_tests.cpp bswap_tests.cpp
caches_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

View File

@ -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)

85
src/test/chain_tests.cpp Normal file
View File

@ -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()

View File

@ -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;

View File

@ -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.

View File

@ -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;
} }

View File

@ -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>();

View File

@ -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);
} }

View File

@ -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&) {
} }
} }

View File

@ -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(

View File

@ -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

View File

@ -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) {

View File

@ -90,22 +90,22 @@ CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
return scriptwitness; return scriptwitness;
} }
const std::map<std::string, unsigned int> FLAG_NAMES = { const std::map<std::string, script_verify_flag_name> FLAG_NAMES = {
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH}, {std::string("P2SH"), SCRIPT_VERIFY_P2SH},
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG}, {std::string("DERSIG"), SCRIPT_VERIFY_DERSIG},
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY}, {std::string("NULLDUMMY"), SCRIPT_VERIFY_NULLDUMMY},
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY}, {std::string("CHECKLOCKTIMEVERIFY"), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY}, {std::string("CHECKSEQUENCEVERIFY"), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, {std::string("WITNESS"), SCRIPT_VERIFY_WITNESS},
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT}, {std::string("TAPROOT"), SCRIPT_VERIFY_TAPROOT},
}; };
std::vector<unsigned int> AllFlags() std::vector<script_verify_flags> AllFlags()
{ {
std::vector<unsigned int> ret; std::vector<script_verify_flags> ret;
for (unsigned int i = 0; i < 128; ++i) { for (unsigned int i = 0; i < 128; ++i) {
unsigned int flag = 0; script_verify_flags flag = 0;
if (i & 1) flag |= SCRIPT_VERIFY_P2SH; if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY; if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
@ -125,13 +125,13 @@ std::vector<unsigned int> AllFlags()
return ret; return ret;
} }
const std::vector<unsigned int> ALL_FLAGS = AllFlags(); const std::vector<script_verify_flags> ALL_FLAGS = AllFlags();
unsigned int ParseScriptFlags(const std::string& str) script_verify_flags ParseScriptFlags(const std::string& str)
{ {
if (str.empty()) return 0; if (str.empty()) return 0;
unsigned int flags = 0; script_verify_flags flags = 0;
std::vector<std::string> words = SplitString(str, ','); std::vector<std::string> words = SplitString(str, ',');
for (const std::string& word : words) { for (const std::string& word : words) {
@ -153,7 +153,7 @@ void Test(const std::string& str)
if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts"); if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
size_t idx = test["index"].getInt<int64_t>(); size_t idx = test["index"].getInt<int64_t>();
if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index"); if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str()); script_verify_flags test_flags = ParseScriptFlags(test["flags"].get_str());
bool final = test.exists("final") && test["final"].get_bool(); bool final = test.exists("final") && test["final"].get_bool();
if (test.exists("success")) { if (test.exists("success")) {

View File

@ -15,6 +15,15 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
static DataStream& operator>>(DataStream& ds, script_verify_flags& f)
{
script_verify_flags::value_type n{0};
ds >> n;
f = script_verify_flags::from_int(n);
assert(n == f.as_int());
return ds;
}
FUZZ_TARGET(script_flags) FUZZ_TARGET(script_flags)
{ {
if (buffer.size() > 100'000) return; if (buffer.size() > 100'000) return;
@ -22,12 +31,14 @@ FUZZ_TARGET(script_flags)
try { try {
const CTransaction tx(deserialize, TX_WITH_WITNESS, ds); const CTransaction tx(deserialize, TX_WITH_WITNESS, ds);
unsigned int verify_flags; script_verify_flags verify_flags;
ds >> verify_flags; ds >> verify_flags;
assert(verify_flags == script_verify_flags::from_int(verify_flags.as_int()));
if (!IsValidFlagCombination(verify_flags)) return; if (!IsValidFlagCombination(verify_flags)) return;
unsigned int fuzzed_flags; script_verify_flags fuzzed_flags;
ds >> fuzzed_flags; ds >> fuzzed_flags;
std::vector<CTxOut> spent_outputs; std::vector<CTxOut> spent_outputs;

View File

@ -51,7 +51,7 @@ public:
FUZZ_TARGET(signature_checker) FUZZ_TARGET(signature_checker)
{ {
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 SigVersion sig_version = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}); const SigVersion sig_version = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0});
const auto script_1{ConsumeScript(fuzzed_data_provider)}; const auto script_1{ConsumeScript(fuzzed_data_provider)};
const auto script_2{ConsumeScript(fuzzed_data_provider)}; const auto script_2{ConsumeScript(fuzzed_data_provider)};

View File

@ -296,7 +296,6 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
std::set<CTransactionRef> added; std::set<CTransactionRef> added;
auto txr = std::make_shared<TransactionsDelta>(removed, added); auto txr = std::make_shared<TransactionsDelta>(removed, added);
node.validation_signals->RegisterSharedValidationInterface(txr); node.validation_signals->RegisterSharedValidationInterface(txr);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
// Make sure ProcessNewPackage on one transaction works. // Make sure ProcessNewPackage on one transaction works.
// The result is not guaranteed to be the same as what is returned by ATMP. // The result is not guaranteed to be the same as what is returned by ATMP.
@ -311,7 +310,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);
} }
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false));
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->SyncWithValidationInterfaceQueue();
node.validation_signals->UnregisterSharedValidationInterface(txr); node.validation_signals->UnregisterSharedValidationInterface(txr);
@ -394,6 +393,9 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
chainstate.SetMempool(&tx_pool); chainstate.SetMempool(&tx_pool);
// If we ever bypass limits, do not do TRUC invariants checks
bool ever_bypassed_limits{false};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300) LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
{ {
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids); const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
@ -412,13 +414,17 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
tx_pool.PrioritiseTransaction(txid, delta); tx_pool.PrioritiseTransaction(txid, delta);
} }
const bool bypass_limits{fuzzed_data_provider.ConsumeBool()};
ever_bypassed_limits |= bypass_limits;
const auto tx = MakeTransactionRef(mut_tx); const auto tx = MakeTransactionRef(mut_tx);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
if (accepted) { if (accepted) {
txids.push_back(tx->GetHash()); txids.push_back(tx->GetHash());
CheckMempoolTRUCInvariants(tx_pool); if (!ever_bypassed_limits) {
CheckMempoolTRUCInvariants(tx_pool);
}
} }
} }
Finish(fuzzed_data_provider, tx_pool, chainstate); Finish(fuzzed_data_provider, tx_pool, chainstate);

View File

@ -7,6 +7,7 @@ add_library(test_fuzz STATIC EXCLUDE_FROM_ALL
descriptor.cpp descriptor.cpp
mempool.cpp mempool.cpp
net.cpp net.cpp
threadinterrupt.cpp
../fuzz.cpp ../fuzz.cpp
../util.cpp ../util.cpp
) )

View File

@ -312,6 +312,33 @@ std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) co
SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos); SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos);
return std::unique_ptr<FuzzedSock>(); return std::unique_ptr<FuzzedSock>();
} }
if (addr != nullptr) {
// Set a fuzzed address in the output argument addr.
memset(addr, 0x00, *addr_len);
if (m_fuzzed_data_provider.ConsumeBool()) {
// IPv4
const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in));
if (*addr_len >= write_len) {
*addr_len = write_len;
auto addr4 = reinterpret_cast<sockaddr_in*>(addr);
addr4->sin_family = AF_INET;
const auto sin_addr_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(sizeof(addr4->sin_addr));
memcpy(&addr4->sin_addr, sin_addr_bytes.data(), sin_addr_bytes.size());
addr4->sin_port = m_fuzzed_data_provider.ConsumeIntegralInRange<uint16_t>(1, 65535);
}
} else {
// IPv6
const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in6));
if (*addr_len >= write_len) {
*addr_len = write_len;
auto addr6 = reinterpret_cast<sockaddr_in6*>(addr);
addr6->sin6_family = AF_INET6;
const auto sin_addr_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(sizeof(addr6->sin6_addr));
memcpy(&addr6->sin6_addr, sin_addr_bytes.data(), sin_addr_bytes.size());
addr6->sin6_port = m_fuzzed_data_provider.ConsumeIntegralInRange<uint16_t>(1, 65535);
}
}
}
return std::make_unique<FuzzedSock>(m_fuzzed_data_provider); return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
} }

View File

@ -139,6 +139,25 @@ public:
} }
}; };
class FuzzedNetEvents : public NetEventsInterface
{
public:
FuzzedNetEvents(FuzzedDataProvider& fdp) : m_fdp(fdp) {}
virtual void InitializeNode(const CNode&, ServiceFlags) override {}
virtual void FinalizeNode(const CNode&) override {}
virtual bool HasAllDesirableServiceFlags(ServiceFlags) const override { return m_fdp.ConsumeBool(); }
virtual bool ProcessMessages(CNode*, std::atomic<bool>&) override { return m_fdp.ConsumeBool(); }
virtual bool SendMessages(CNode*) override { return m_fdp.ConsumeBool(); }
private:
FuzzedDataProvider& m_fdp;
};
class FuzzedSock : public Sock class FuzzedSock : public Sock
{ {
FuzzedDataProvider& m_fuzzed_data_provider; FuzzedDataProvider& m_fuzzed_data_provider;
@ -203,6 +222,11 @@ public:
bool IsConnected(std::string& errmsg) const override; bool IsConnected(std::string& errmsg) const override;
}; };
[[nodiscard]] inline FuzzedNetEvents ConsumeNetEvents(FuzzedDataProvider& fdp) noexcept
{
return FuzzedNetEvents{fdp};
}
[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider) [[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
{ {
return FuzzedSock{fuzzed_data_provider}; return FuzzedSock{fuzzed_data_provider};
@ -225,6 +249,18 @@ inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcep
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
} }
inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_data_provider,
size_t max_vector_size = 5) noexcept
{
std::vector<CService> ret;
const size_t size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
ret.reserve(size);
for (size_t i = 0; i < size; ++i) {
ret.emplace_back(ConsumeService(fuzzed_data_provider));
}
return ret;
}
CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept; CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept;
template <bool ReturnUniquePtr = false> template <bool ReturnUniquePtr = false>
@ -239,6 +275,8 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES); const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false}; const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
if constexpr (ReturnUniquePtr) { if constexpr (ReturnUniquePtr) {
return std::make_unique<CNode>(node_id, return std::make_unique<CNode>(node_id,
@ -250,6 +288,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
addr_name, addr_name,
conn_type, conn_type,
inbound_onion, inbound_onion,
network_id,
CNodeOptions{ .permission_flags = permission_flags }); CNodeOptions{ .permission_flags = permission_flags });
} else { } else {
return CNode{node_id, return CNode{node_id,
@ -261,6 +300,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
addr_name, addr_name,
conn_type, conn_type,
inbound_onion, inbound_onion,
network_id,
CNodeOptions{ .permission_flags = permission_flags }}; CNodeOptions{ .permission_flags = permission_flags }};
} }
} }

View File

@ -0,0 +1,22 @@
// Copyright (c) 2024-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.
#include <test/fuzz/util.h>
#include <test/fuzz/util/threadinterrupt.h>
FuzzedThreadInterrupt::FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
: m_fuzzed_data_provider{fuzzed_data_provider}
{
}
bool FuzzedThreadInterrupt::interrupted() const
{
return m_fuzzed_data_provider.ConsumeBool();
}
bool FuzzedThreadInterrupt::sleep_for(Clock::duration)
{
SetMockTime(ConsumeTime(m_fuzzed_data_provider)); // Time could go backwards.
return m_fuzzed_data_provider.ConsumeBool();
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2024-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_TEST_FUZZ_UTIL_THREADINTERRUPT_H
#define BITCOIN_TEST_FUZZ_UTIL_THREADINTERRUPT_H
#include <test/fuzz/FuzzedDataProvider.h>
#include <util/threadinterrupt.h>
#include <memory>
/**
* Mocked CThreadInterrupt that returns "randomly" whether it is interrupted and never sleeps.
*/
class FuzzedThreadInterrupt : public CThreadInterrupt
{
public:
explicit FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider);
virtual bool interrupted() const override;
virtual bool sleep_for(Clock::duration) override;
private:
FuzzedDataProvider& m_fuzzed_data_provider;
};
[[nodiscard]] inline std::shared_ptr<CThreadInterrupt> ConsumeThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
{
return std::make_shared<FuzzedThreadInterrupt>(fuzzed_data_provider);
}
#endif // BITCOIN_TEST_FUZZ_UTIL_THREADINTERRUPT_H

View File

@ -50,10 +50,10 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a')); return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
}; };
CThreadInterrupt interrupt; auto interrupt{std::make_shared<CThreadInterrupt>()};
const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)}; const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
const Proxy sam_proxy(addr.value(), /*tor_stream_isolation=*/false); const Proxy sam_proxy(addr.value(), /*tor_stream_isolation=*/false);
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, &interrupt); i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, interrupt);
{ {
ASSERT_DEBUG_LOG("Creating persistent SAM session"); ASSERT_DEBUG_LOG("Creating persistent SAM session");
@ -112,12 +112,12 @@ BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
// clang-format on // clang-format on
}; };
CThreadInterrupt interrupt; auto interrupt{std::make_shared<CThreadInterrupt>()};
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656}; const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
const Proxy sam_proxy(addr, /*tor_stream_isolation=*/false); const Proxy sam_proxy(addr, /*tor_stream_isolation=*/false);
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
sam_proxy, sam_proxy,
&interrupt); interrupt);
i2p::Connection conn; i2p::Connection conn;
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i) {
@ -155,10 +155,10 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
"391 bytes"}}) { "391 bytes"}}) {
BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents)); BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
CThreadInterrupt interrupt; auto interrupt{std::make_shared<CThreadInterrupt>()};
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656}; const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
const Proxy sam_proxy{addr, /*tor_stream_isolation=*/false}; const Proxy sam_proxy{addr, /*tor_stream_isolation=*/false};
i2p::sam::Session session(i2p_private_key_file, sam_proxy, &interrupt); i2p::sam::Session session(i2p_private_key_file, sam_proxy, interrupt);
{ {
ASSERT_DEBUG_LOG("Creating persistent SAM session"); ASSERT_DEBUG_LOG("Creating persistent SAM session");

View File

@ -38,7 +38,7 @@ sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const
BOOST_AUTO_TEST_CASE(multisig_verify) BOOST_AUTO_TEST_CASE(multisig_verify)
{ {
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; script_verify_flags flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
ScriptError err; ScriptError err;
CKey key[4]; CKey key[4];

View File

@ -72,7 +72,8 @@ void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, Connm
CAddress{}, CAddress{},
/*addrNameIn=*/"", /*addrNameIn=*/"",
conn_type, conn_type,
/*inbound_onion=*/inbound_onion}); /*inbound_onion=*/inbound_onion,
/*network_key=*/0});
CNode& node = *nodes.back(); CNode& node = *nodes.back();
node.SetCommonVersion(PROTOCOL_VERSION); node.SetCommonVersion(PROTOCOL_VERSION);
@ -151,15 +152,8 @@ BOOST_FIXTURE_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection,
} }
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected"); BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
for (auto node : connman->TestNodes()) { for (const auto& node : connman->TestNodes()) {
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr)); BOOST_CHECK(connman->AlreadyConnectedToAddressPublic(node->addr));
}
BOOST_TEST_MESSAGE("\nCheck that peers with the same addresses as connected peers but different ports are detected as connected.");
for (auto node : connman->TestNodes()) {
uint16_t changed_port = node->addr.GetPort() + 1;
CService address_with_changed_port{node->addr, changed_port};
BOOST_CHECK(connman->AlreadyConnectedPublic(CAddress{address_with_changed_port, NODE_NONE}));
} }
// Clean up // Clean up

View File

@ -67,7 +67,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress(), CAddress(),
pszDest, pszDest,
ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false); /*inbound_onion=*/false,
/*network_key=*/0);
BOOST_CHECK(pnode1->IsFullOutboundConn() == true); BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
BOOST_CHECK(pnode1->IsManualConn() == false); BOOST_CHECK(pnode1->IsManualConn() == false);
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false); BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
@ -85,7 +86,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress(), CAddress(),
pszDest, pszDest,
ConnectionType::INBOUND, ConnectionType::INBOUND,
/*inbound_onion=*/false); /*inbound_onion=*/false,
/*network_key=*/1);
BOOST_CHECK(pnode2->IsFullOutboundConn() == false); BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
BOOST_CHECK(pnode2->IsManualConn() == false); BOOST_CHECK(pnode2->IsManualConn() == false);
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false); BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
@ -103,7 +105,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress(), CAddress(),
pszDest, pszDest,
ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false); /*inbound_onion=*/false,
/*network_key=*/2);
BOOST_CHECK(pnode3->IsFullOutboundConn() == true); BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
BOOST_CHECK(pnode3->IsManualConn() == false); BOOST_CHECK(pnode3->IsManualConn() == false);
BOOST_CHECK(pnode3->IsBlockOnlyConn() == false); BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
@ -121,7 +124,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress(), CAddress(),
pszDest, pszDest,
ConnectionType::INBOUND, ConnectionType::INBOUND,
/*inbound_onion=*/true); /*inbound_onion=*/true,
/*network_key=*/3);
BOOST_CHECK(pnode4->IsFullOutboundConn() == false); BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
BOOST_CHECK(pnode4->IsManualConn() == false); BOOST_CHECK(pnode4->IsManualConn() == false);
BOOST_CHECK(pnode4->IsBlockOnlyConn() == false); BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
@ -613,7 +617,8 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
CAddress{}, CAddress{},
/*pszDest=*/std::string{}, /*pszDest=*/std::string{},
ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false); /*inbound_onion=*/false,
/*network_key=*/0);
pnode->fSuccessfullyConnected.store(true); pnode->fSuccessfullyConnected.store(true);
// the peer claims to be reaching us via IPv6 // the peer claims to be reaching us via IPv6
@ -667,7 +672,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
/*addrBindIn=*/CService{}, /*addrBindIn=*/CService{},
/*addrNameIn=*/std::string{}, /*addrNameIn=*/std::string{},
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY, /*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false}; /*inbound_onion=*/false,
/*network_key=*/0};
peer_out.fSuccessfullyConnected = true; peer_out.fSuccessfullyConnected = true;
peer_out.SetAddrLocal(peer_us); peer_out.SetAddrLocal(peer_us);
@ -688,7 +694,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
/*addrBindIn=*/CService{}, /*addrBindIn=*/CService{},
/*addrNameIn=*/std::string{}, /*addrNameIn=*/std::string{},
/*conn_type_in=*/ConnectionType::INBOUND, /*conn_type_in=*/ConnectionType::INBOUND,
/*inbound_onion=*/false}; /*inbound_onion=*/false,
/*network_key=*/1};
peer_in.fSuccessfullyConnected = true; peer_in.fSuccessfullyConnected = true;
peer_in.SetAddrLocal(peer_us); peer_in.SetAddrLocal(peer_us);
@ -825,7 +832,8 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
/*addrBindIn=*/CService{}, /*addrBindIn=*/CService{},
/*addrNameIn=*/std::string{}, /*addrNameIn=*/std::string{},
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY, /*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false}; /*inbound_onion=*/false,
/*network_key=*/2};
const uint64_t services{NODE_NETWORK | NODE_WITNESS}; const uint64_t services{NODE_NETWORK | NODE_WITNESS};
const int64_t time{0}; const int64_t time{0};
@ -900,7 +908,8 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
CAddress{}, CAddress{},
/*pszDest=*/std::string{}, /*pszDest=*/std::string{},
ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false); /*inbound_onion=*/false,
/*network_key=*/0);
}; };
g_reachable_nets.Add(NET_CJDNS); g_reachable_nets.Add(NET_CJDNS);

View File

@ -25,7 +25,7 @@
#include <univalue.h> #include <univalue.h>
unsigned int ParseScriptFlags(std::string strFlags); script_verify_flags ParseScriptFlags(std::string strFlags);
BOOST_AUTO_TEST_SUITE(script_assets_tests) BOOST_AUTO_TEST_SUITE(script_assets_tests)
@ -71,12 +71,12 @@ static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
return scriptwitness; return scriptwitness;
} }
static std::vector<unsigned int> AllConsensusFlags() static std::vector<script_verify_flags> AllConsensusFlags()
{ {
std::vector<unsigned int> ret; std::vector<script_verify_flags> ret;
for (unsigned int i = 0; i < 128; ++i) { for (unsigned int i = 0; i < 128; ++i) {
unsigned int flag = 0; script_verify_flags flag = 0;
if (i & 1) flag |= SCRIPT_VERIFY_P2SH; if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY; if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
@ -97,7 +97,7 @@ static std::vector<unsigned int> AllConsensusFlags()
} }
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */ /** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags(); static const std::vector<script_verify_flags> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
static void AssetTest(const UniValue& test, SignatureCache& signature_cache) static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
{ {
@ -107,7 +107,7 @@ static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]); const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
BOOST_CHECK(prevouts.size() == mtx.vin.size()); BOOST_CHECK(prevouts.size() == mtx.vin.size());
size_t idx = test["index"].getInt<int64_t>(); size_t idx = test["index"].getInt<int64_t>();
uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())}; script_verify_flags test_flags{ParseScriptFlags(test["flags"].get_str())};
bool fin = test.exists("final") && test["final"].get_bool(); bool fin = test.exists("final") && test["final"].get_bool();
if (test.exists("success")) { if (test.exists("success")) {

View File

@ -9,6 +9,7 @@
#include <core_io.h> #include <core_io.h>
#include <key.h> #include <key.h>
#include <rpc/util.h> #include <rpc/util.h>
#include <script/interpreter.h>
#include <script/script.h> #include <script/script.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <script/sigcache.h> #include <script/sigcache.h>
@ -22,6 +23,7 @@
#include <test/util/transaction_utils.h> #include <test/util/transaction_utils.h>
#include <util/fs.h> #include <util/fs.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/string.h>
#include <cstdint> #include <cstdint>
#include <fstream> #include <fstream>
@ -38,10 +40,9 @@
using namespace util::hex_literals; using namespace util::hex_literals;
static const unsigned int gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; static const script_verify_flags gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
unsigned int ParseScriptFlags(std::string strFlags); script_verify_flags ParseScriptFlags(std::string strFlags);
std::string FormatScriptFlags(unsigned int flags);
struct ScriptErrorDesc struct ScriptErrorDesc
{ {
@ -95,6 +96,11 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"}, {SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
}; };
static std::string FormatScriptFlags(script_verify_flags flags)
{
return util::Join(GetScriptFlagNames(flags), ",");
}
static std::string FormatScriptError(ScriptError_t err) static std::string FormatScriptError(ScriptError_t err)
{ {
for (const auto& se : script_errors) for (const auto& se : script_errors)
@ -114,7 +120,7 @@ static ScriptError_t ParseScriptError(const std::string& name)
} }
struct ScriptTest : BasicTestingSetup { struct ScriptTest : BasicTestingSetup {
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, uint32_t flags, const std::string& message, int scriptError, CAmount nValue = 0) void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, script_verify_flags flags, const std::string& message, int scriptError, CAmount nValue = 0)
{ {
bool expect = (scriptError == SCRIPT_ERR_OK); bool expect = (scriptError == SCRIPT_ERR_OK);
if (flags & SCRIPT_VERIFY_CLEANSTACK) { if (flags & SCRIPT_VERIFY_CLEANSTACK) {
@ -128,13 +134,13 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message); BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result. // Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 256; ++i) {
uint32_t extra_flags(m_rng.randbits(16)); script_verify_flags extra_flags = script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS));
uint32_t combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)}; script_verify_flags combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
// Weed out some invalid flag combinations. // Weed out some invalid flag combinations.
if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue; if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue; if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags)); BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags.as_int()));
} }
} }
}; // struct ScriptTest }; // struct ScriptTest
@ -226,7 +232,7 @@ private:
bool havePush{false}; bool havePush{false};
std::vector<unsigned char> push; std::vector<unsigned char> push;
std::string comment; std::string comment;
uint32_t flags; script_verify_flags flags;
int scriptError{SCRIPT_ERR_OK}; int scriptError{SCRIPT_ERR_OK};
CAmount nValue; CAmount nValue;
@ -246,7 +252,7 @@ private:
} }
public: public:
TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_) TestBuilder(const CScript& script_, const std::string& comment_, script_verify_flags flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
{ {
CScript scriptPubKey = script; CScript scriptPubKey = script;
if (wm == WitnessMode::PKH) { if (wm == WitnessMode::PKH) {
@ -963,7 +969,7 @@ BOOST_AUTO_TEST_CASE(script_json_test)
} else { } else {
scriptPubKey = ParseScript(scriptPubKeyString); scriptPubKey = ParseScript(scriptPubKeyString);
} }
unsigned int scriptflags = ParseScriptFlags(test[pos++].get_str()); script_verify_flags scriptflags = ParseScriptFlags(test[pos++].get_str());
int scriptError = ParseScriptError(test[pos++].get_str()); int scriptError = ParseScriptError(test[pos++].get_str());
DoTest(scriptPubKey, scriptSig, witness, scriptflags, strTest, scriptError, nValue); DoTest(scriptPubKey, scriptSig, witness, scriptflags, strTest, scriptError, nValue);
@ -1706,4 +1712,15 @@ BOOST_AUTO_TEST_CASE(compute_tapleaf)
BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, std::span(script)), tlc2); BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, std::span(script)), tlc2);
} }
BOOST_AUTO_TEST_CASE(formatscriptflags)
{
// quick check that FormatScriptFlags reports any unknown/unexpected bits
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH), "P2SH");
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_TAPROOT), "P2SH,TAPROOT");
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | script_verify_flags::from_int(1u<<31)), "P2SH,0x80000000");
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int(1u<<27)), "TAPROOT,0x08000000");
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int((1u<<28) | (1ull<<58))), "TAPROOT,0x400000010000000");
BOOST_CHECK_EQUAL(FormatScriptFlags(script_verify_flags::from_int(1u<<26)), "0x04000000");
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
* Verifies script execution of the zeroth scriptPubKey of tx output and * Verifies script execution of the zeroth scriptPubKey of tx output and
* zeroth scriptSig and witness of tx input. * zeroth scriptSig and witness of tx input.
*/ */
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, uint32_t flags) static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, script_verify_flags flags)
{ {
ScriptError error; ScriptError error;
CTransaction inputi(input); CTransaction inputi(input);
@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CKey key = GenerateRandomKey(); CKey key = GenerateRandomKey();
CPubKey pubkey = key.GetPubKey(); CPubKey pubkey = key.GetPubKey();
// Default flags // Default flags
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH}; const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
// Multisig script (legacy counting) // Multisig script (legacy counting)
{ {

View File

@ -17,6 +17,7 @@
#include <policy/policy.h> #include <policy/policy.h>
#include <policy/settings.h> #include <policy/settings.h>
#include <primitives/transaction_identifier.h> #include <primitives/transaction_identifier.h>
#include <script/interpreter.h>
#include <script/script.h> #include <script/script.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <script/sigcache.h> #include <script/sigcache.h>
@ -49,41 +50,21 @@ typedef std::vector<unsigned char> valtype;
static CFeeRate g_dust{DUST_RELAY_TX_FEE}; static CFeeRate g_dust{DUST_RELAY_TX_FEE};
static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG}; static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG};
static std::map<std::string, unsigned int> mapFlagNames = { static const std::map<std::string, script_verify_flag_name>& mapFlagNames = g_verify_flag_names;
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
{std::string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S},
{std::string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY},
{std::string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA},
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
{std::string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
{std::string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK},
{std::string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF},
{std::string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL},
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
{std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
};
unsigned int ParseScriptFlags(std::string strFlags) script_verify_flags ParseScriptFlags(std::string strFlags)
{ {
unsigned int flags = SCRIPT_VERIFY_NONE; script_verify_flags flags = SCRIPT_VERIFY_NONE;
if (strFlags.empty() || strFlags == "NONE") return flags; if (strFlags.empty() || strFlags == "NONE") return flags;
std::vector<std::string> words = SplitString(strFlags, ','); std::vector<std::string> words = SplitString(strFlags, ',');
for (const std::string& word : words) for (const std::string& word : words)
{ {
if (!mapFlagNames.count(word)) if (!mapFlagNames.count(word)) {
BOOST_ERROR("Bad test: unknown verification flag '" << word << "'"); BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
flags |= mapFlagNames[word]; continue;
}
flags |= mapFlagNames.at(word);
} }
return flags; return flags;
} }
@ -91,34 +72,18 @@ unsigned int ParseScriptFlags(std::string strFlags)
// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames. // Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
bool CheckMapFlagNames() bool CheckMapFlagNames()
{ {
unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS}; script_verify_flags standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
for (const auto& pair : mapFlagNames) { for (const auto& pair : mapFlagNames) {
standard_flags_missing &= ~(pair.second); standard_flags_missing &= ~(pair.second);
} }
return standard_flags_missing == 0; return standard_flags_missing == 0;
} }
std::string FormatScriptFlags(unsigned int flags)
{
if (flags == SCRIPT_VERIFY_NONE) {
return "";
}
std::string ret;
std::map<std::string, unsigned int>::const_iterator it = mapFlagNames.begin();
while (it != mapFlagNames.end()) {
if (flags & it->second) {
ret += it->first + ",";
}
it++;
}
return ret.substr(0, ret.size() - 1);
}
/* /*
* Check that the input scripts of a transaction are valid/invalid as expected. * Check that the input scripts of a transaction are valid/invalid as expected.
*/ */
bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys, bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags, const std::map<COutPoint, int64_t>& map_prevout_values, script_verify_flags flags,
const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid) const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
{ {
bool tx_valid = true; bool tx_valid = true;
@ -152,18 +117,18 @@ bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>&
* CLEANSTACK must be used WITNESS and P2SH * CLEANSTACK must be used WITNESS and P2SH
*/ */
unsigned int TrimFlags(unsigned int flags) script_verify_flags TrimFlags(script_verify_flags flags)
{ {
// WITNESS requires P2SH // WITNESS requires P2SH
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS; if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~SCRIPT_VERIFY_WITNESS;
// CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH) // CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK; if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~SCRIPT_VERIFY_CLEANSTACK;
Assert(IsValidFlagCombination(flags)); Assert(IsValidFlagCombination(flags));
return flags; return flags;
} }
unsigned int FillFlags(unsigned int flags) script_verify_flags FillFlags(script_verify_flags flags)
{ {
// CLEANSTACK implies WITNESS // CLEANSTACK implies WITNESS
if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS; if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
@ -178,11 +143,11 @@ unsigned int FillFlags(unsigned int flags)
// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are // that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}. // 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
// Assumes that mapFlagNames contains all script verify flags. // Assumes that mapFlagNames contains all script verify flags.
std::set<unsigned int> ExcludeIndividualFlags(unsigned int flags) std::set<script_verify_flags> ExcludeIndividualFlags(script_verify_flags flags)
{ {
std::set<unsigned int> flags_combos; std::set<script_verify_flags> flags_combos;
for (const auto& pair : mapFlagNames) { for (const auto& pair : mapFlagNames) {
const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second)); script_verify_flags flags_excluding_one = TrimFlags(flags & ~(pair.second));
if (flags != flags_excluding_one) { if (flags != flags_excluding_one) {
flags_combos.insert(flags_excluding_one); flags_combos.insert(flags_excluding_one);
} }
@ -247,7 +212,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_CHECK(state.IsValid()); BOOST_CHECK(state.IsValid());
PrecomputedTransactionData txdata(tx); PrecomputedTransactionData txdata(tx);
unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags. // Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
if (~verify_flags != FillFlags(~verify_flags)) { if (~verify_flags != FillFlags(~verify_flags)) {
@ -260,14 +225,14 @@ BOOST_AUTO_TEST_CASE(tx_valid)
// Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction // Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
for (const auto& [name, flag] : mapFlagNames) { for (const auto& [name, flag] : mapFlagNames) {
// Removing individual flags // Removing individual flags
unsigned int flags = TrimFlags(~(verify_flags | flag)); script_verify_flags flags = TrimFlags(~(verify_flags | flag));
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest); BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest);
} }
// Removing random combinations of flags // Removing random combinations of flags
flags = TrimFlags(~(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size()))); flags = TrimFlags(~(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS))));
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags) << ": " << strTest); BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags.as_int()) << ": " << strTest);
} }
} }
@ -337,7 +302,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
} }
PrecomputedTransactionData txdata(tx); PrecomputedTransactionData txdata(tx);
unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags. // Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
if (verify_flags != FillFlags(verify_flags)) { if (verify_flags != FillFlags(verify_flags)) {
@ -350,13 +315,13 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
// Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction // Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
for (const auto& [name, flag] : mapFlagNames) { for (const auto& [name, flag] : mapFlagNames) {
unsigned int flags = FillFlags(verify_flags | flag); script_verify_flags flags = FillFlags(verify_flags | flag);
// Adding individual flags // Adding individual flags
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest); BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest);
} }
// Adding random combinations of flags // Adding random combinations of flags
flags = FillFlags(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size())); flags = FillFlags(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS)));
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest); BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest);
} }
@ -488,7 +453,7 @@ static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack); assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
} }
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, uint32_t flags, bool success) static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, script_verify_flags flags, bool success)
{ {
ScriptError error; ScriptError error;
CTransaction inputi(input); CTransaction inputi(input);

View File

@ -21,7 +21,7 @@ struct Dersig100Setup : public TestChain100Setup {
}; };
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore, const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata, bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache, ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -120,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
// should fail. // should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating // Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code. // any script flag that is implemented as an upgraded NOP code.
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) static void ValidateCheckInputsForAllFlags(const CTransaction &tx, script_verify_flags failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{ {
PrecomputedTransactionData txdata; PrecomputedTransactionData txdata;
@ -130,7 +130,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
TxValidationState state; TxValidationState state;
// Randomly selects flag combinations // Randomly selects flag combinations
uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1); script_verify_flags test_flags = script_verify_flags::from_int(insecure_rand.randrange(MAX_SCRIPT_VERIFY_FLAGS));
// Filter out incompatible flag choices // Filter out incompatible flag choices
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {

View File

@ -116,7 +116,7 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) co
CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type) CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
{ {
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true); CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true, /*proxy_override=*/std::nullopt);
if (!node) return nullptr; if (!node) return nullptr;
node->SetCommonVersion(PROTOCOL_VERSION); node->SetCommonVersion(PROTOCOL_VERSION);
peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS)); peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));

View File

@ -71,6 +71,24 @@ struct ConnmanTestMsg : public CConnman {
m_nodes.clear(); m_nodes.clear();
} }
void CreateNodeFromAcceptedSocketPublic(std::unique_ptr<Sock> sock,
NetPermissionFlags permissions,
const CAddress& addr_bind,
const CAddress& addr_peer)
{
CreateNodeFromAcceptedSocket(std::move(sock), permissions, addr_bind, addr_peer);
}
bool InitBindsPublic(const CConnman::Options& options)
{
return InitBinds(options);
}
void SocketHandlerPublic()
{
SocketHandler();
}
void Handshake(CNode& node, void Handshake(CNode& node,
bool successfully_connected, bool successfully_connected,
ServiceFlags remote_services, ServiceFlags remote_services,
@ -89,7 +107,7 @@ struct ConnmanTestMsg : public CConnman {
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const; bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const;
void FlushSendBuffer(CNode& node) const; void FlushSendBuffer(CNode& node) const;
bool AlreadyConnectedPublic(const CAddress& addr) { return AlreadyConnectedToAddress(addr); }; bool AlreadyConnectedToAddressPublic(const CNetAddr& addr) { return AlreadyConnectedToAddress(addr); };
CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type) CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex); EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);

View File

@ -5,7 +5,7 @@
#include <script/interpreter.h> #include <script/interpreter.h>
#include <test/util/script.h> #include <test/util/script.h>
bool IsValidFlagCombination(unsigned flags) bool IsValidFlagCombination(script_verify_flags flags)
{ {
if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false; if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false;
if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false; if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false;

View File

@ -7,6 +7,7 @@
#include <crypto/sha256.h> #include <crypto/sha256.h>
#include <script/script.h> #include <script/script.h>
#include <script/verify_flags.h>
static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}}; static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
static const CScript P2WSH_OP_TRUE{ static const CScript P2WSH_OP_TRUE{
@ -31,6 +32,6 @@ static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_ca
static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}}; static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
/** Flags that are not forbidden by an assert in script validation */ /** Flags that are not forbidden by an assert in script validation */
bool IsValidFlagCombination(unsigned flags); bool IsValidFlagCombination(script_verify_flags flags);
#endif // BITCOIN_TEST_UTIL_SCRIPT_H #endif // BITCOIN_TEST_UTIL_SCRIPT_H

View File

@ -9,11 +9,16 @@
CThreadInterrupt::CThreadInterrupt() : flag(false) {} CThreadInterrupt::CThreadInterrupt() : flag(false) {}
CThreadInterrupt::operator bool() const bool CThreadInterrupt::interrupted() const
{ {
return flag.load(std::memory_order_acquire); return flag.load(std::memory_order_acquire);
} }
CThreadInterrupt::operator bool() const
{
return interrupted();
}
void CThreadInterrupt::reset() void CThreadInterrupt::reset()
{ {
flag.store(false, std::memory_order_release); flag.store(false, std::memory_order_release);

View File

@ -27,11 +27,27 @@ class CThreadInterrupt
{ {
public: public:
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
CThreadInterrupt(); CThreadInterrupt();
explicit operator bool() const;
void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut); virtual ~CThreadInterrupt() = default;
void reset();
bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut); /// Return true if `operator()()` has been called.
virtual bool interrupted() const;
/// An alias for `interrupted()`.
virtual explicit operator bool() const;
/// Interrupt any sleeps. After this `interrupted()` will return `true`.
virtual void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
/// Reset to an non-interrupted state.
virtual void reset();
/// Sleep for the given duration.
/// @retval true The time passed.
/// @retval false The sleep was interrupted.
virtual bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
private: private:
std::condition_variable cond; std::condition_variable cond;

View File

@ -139,7 +139,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
} }
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore, const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata, bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache, ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks = nullptr) std::vector<CScriptCheck>* pvChecks = nullptr)
@ -262,9 +262,6 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time}); return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time});
} }
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache) static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
{ {
@ -398,7 +395,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
* */ * */
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state, static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& view, const CTxMemPool& pool, const CCoinsViewCache& view, const CTxMemPool& pool,
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip, script_verify_flags flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
ValidationCache& validation_cache) ValidationCache& validation_cache)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{ {
@ -1044,26 +1041,28 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Even though just checking direct mempool parents for inheritance would be sufficient, we // Even though just checking direct mempool parents for inheritance would be sufficient, we
// check using the full ancestor set here because it's more convenient to use what we have // check using the full ancestor set here because it's more convenient to use what we have
// already calculated. // already calculated.
if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { if (!args.m_bypass_limits) {
// Single transaction contexts only. if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
if (args.m_allow_sibling_eviction && err->second != nullptr) { // Single transaction contexts only.
// We should only be considering where replacement is considered valid as well. if (args.m_allow_sibling_eviction && err->second != nullptr) {
Assume(args.m_allow_replacement); // We should only be considering where replacement is considered valid as well.
Assume(args.m_allow_replacement);
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be // Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
// included in RBF checks. // included in RBF checks.
ws.m_conflicts.insert(err->second->GetHash()); ws.m_conflicts.insert(err->second->GetHash());
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards // Adding the sibling to m_iters_conflicting here means that it doesn't count towards
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
// the descendant count is done separately in SingleTRUCChecks for TRUC transactions. // the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value()); ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
ws.m_sibling_eviction = true; ws.m_sibling_eviction = true;
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks. // The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
// Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC // Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
// (which is normally done in PreChecks). However, the only way a TRUC transaction can // (which is normally done in PreChecks). However, the only way a TRUC transaction can
// have a non-TRUC and non-BIP125 descendant is due to a reorg. // have a non-TRUC and non-BIP125 descendant is due to a reorg.
} else { } else {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first); return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
}
} }
} }
@ -1249,7 +1248,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
const CTransaction& tx = *ws.m_ptx; const CTransaction& tx = *ws.m_ptx;
TxValidationState& state = ws.m_state; TxValidationState& state = ws.m_state;
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; constexpr script_verify_flags scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
// Check input scripts and signatures. // Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks. // This is done last to help prevent CPU exhaustion denial-of-service attacks.
@ -1288,7 +1287,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
// There is a similar check in CreateNewBlock() to prevent creating // There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such // invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack. // transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)}; script_verify_flags currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) { ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString()); LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
@ -2096,7 +2095,7 @@ std::optional<std::pair<ScriptError, std::string>> CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR}; ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR};
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) { if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, m_flags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
return std::nullopt; return std::nullopt;
} else { } else {
auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n); auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n);
@ -2140,7 +2139,7 @@ ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, cons
* Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp * Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp
*/ */
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore, const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata, bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache, ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks) std::vector<CScriptCheck>* pvChecks)
@ -2328,7 +2327,7 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
} }
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman) script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
{ {
const Consensus::Params& consensusparams = chainman.GetConsensus(); const Consensus::Params& consensusparams = chainman.GetConsensus();
@ -2340,7 +2339,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
// mainnet. // mainnet.
// For simplicity, always leave P2SH+WITNESS+TAPROOT on except for the two // For simplicity, always leave P2SH+WITNESS+TAPROOT on except for the two
// violating blocks. // violating blocks.
uint32_t flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT}; script_verify_flags flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
const auto it{consensusparams.script_flag_exceptions.find(*Assert(block_index.phashBlock))}; const auto it{consensusparams.script_flag_exceptions.find(*Assert(block_index.phashBlock))};
if (it != consensusparams.script_flag_exceptions.end()) { if (it != consensusparams.script_flag_exceptions.end()) {
flags = it->second; flags = it->second;
@ -2554,7 +2553,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
} }
// Get the script flags for this block // Get the script flags for this block
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)}; script_verify_flags flags{GetBlockScriptFlags(*pindex, m_chainman)};
const auto time_2{SteadyClock::now()}; const auto time_2{SteadyClock::now()};
m_chainman.time_forks += time_2 - time_1; m_chainman.time_forks += time_2 - time_1;

View File

@ -23,6 +23,7 @@
#include <policy/policy.h> #include <policy/policy.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <script/sigcache.h> #include <script/sigcache.h>
#include <script/verify_flags.h>
#include <sync.h> #include <sync.h>
#include <txdb.h> #include <txdb.h>
#include <txmempool.h> #include <txmempool.h>
@ -336,14 +337,14 @@ private:
CTxOut m_tx_out; CTxOut m_tx_out;
const CTransaction *ptxTo; const CTransaction *ptxTo;
unsigned int nIn; unsigned int nIn;
unsigned int nFlags; script_verify_flags m_flags;
bool cacheStore; bool cacheStore;
PrecomputedTransactionData *txdata; PrecomputedTransactionData *txdata;
SignatureCache* m_signature_cache; SignatureCache* m_signature_cache;
public: public:
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, script_verify_flags flags, bool cacheIn, PrecomputedTransactionData* txdataIn) :
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { } m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), m_flags(flags), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
CScriptCheck(const CScriptCheck&) = delete; CScriptCheck(const CScriptCheck&) = delete;
CScriptCheck& operator=(const CScriptCheck&) = delete; CScriptCheck& operator=(const CScriptCheck&) = delete;
@ -1365,4 +1366,7 @@ bool IsBIP30Repeat(const CBlockIndex& block_index);
/** Identifies blocks which coinbase output was subsequently overwritten in the UTXO set (see BIP30) */ /** Identifies blocks which coinbase output was subsequently overwritten in the UTXO set (see BIP30) */
bool IsBIP30Unspendable(const uint256& block_hash, int block_height); bool IsBIP30Unspendable(const uint256& block_hash, int block_height);
// Returns the script flags which should be checked for a given block
script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
#endif // BITCOIN_VALIDATION_H #endif // BITCOIN_VALIDATION_H

View File

@ -51,6 +51,8 @@ bool VerifyWallets(WalletContext& context)
} }
LogInfo("Using wallet directory %s", fs::PathToString(GetWalletDir())); LogInfo("Using wallet directory %s", fs::PathToString(GetWalletDir()));
// Print general DB information
LogDBInfo();
chain.initMessage(_("Verifying wallet(s)…")); chain.initMessage(_("Verifying wallet(s)…"));

View File

@ -116,9 +116,6 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa
{ {
{ {
LOCK(g_sqlite_mutex); LOCK(g_sqlite_mutex);
LogInfo("Using SQLite Version %s", SQLiteDatabaseVersion());
LogInfo("Using wallet %s", fs::PathToString(m_dir_path));
if (++g_sqlite_count == 1) { if (++g_sqlite_count == 1) {
// Setup logging // Setup logging
int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr); int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr);

View File

@ -63,6 +63,12 @@ const std::string WATCHS{"watchs"};
const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS}; const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS};
} // namespace DBKeys } // namespace DBKeys
void LogDBInfo()
{
// Add useful DB information here. This will be printed during startup.
LogInfo("Using SQLite Version %s", SQLiteDatabaseVersion());
}
// //
// WalletBatch // WalletBatch
// //

View File

@ -27,6 +27,9 @@ class CWallet;
class CWalletTx; class CWalletTx;
struct WalletContext; struct WalletContext;
// Logs information about the database, including available engines, features, and other capabilities
void LogDBInfo();
/** /**
* Overview of wallet database classes: * Overview of wallet database classes:
* *

View File

@ -39,37 +39,41 @@ class BindExtraTest(BitcoinTestFramework):
loopback_ipv4 = addr_to_hex("127.0.0.1") loopback_ipv4 = addr_to_hex("127.0.0.1")
# Start custom ports by reusing unused p2p ports # Start custom ports by reusing unused p2p ports
port = p2p_port(self.num_nodes) def extra_port():
port = p2p_port(extra_port.index)
extra_port.index += 1
return port
extra_port.index = self.num_nodes
# Array of tuples [command line arguments, expected bind addresses]. # Array of tuples [command line arguments, expected bind addresses].
self.expected = [] self.expected = []
# Node0, no normal -bind=... with -bind=...=onion, thus only the tor target. # Node0, no normal -bind=... with -bind=...=onion, thus only the tor target.
port = extra_port()
self.expected.append( self.expected.append(
[ [
[f"-bind=127.0.0.1:{port}=onion"], [f"-bind=127.0.0.1:{port}=onion"],
[(loopback_ipv4, port)] [(loopback_ipv4, port)],
], ],
) )
port += 1
# Node1, both -bind=... and -bind=...=onion. # Node1, both -bind=... and -bind=...=onion.
port = [extra_port(), extra_port()]
self.expected.append( self.expected.append(
[ [
[f"-bind=127.0.0.1:{port}", f"-bind=127.0.0.1:{port + 1}=onion"], [f"-bind=127.0.0.1:{port[0]}", f"-bind=127.0.0.1:{port[1]}=onion"],
[(loopback_ipv4, port), (loopback_ipv4, port + 1)] [(loopback_ipv4, port[0]), (loopback_ipv4, port[1])],
], ],
) )
port += 2
# Node2, no -bind=...=onion, thus no extra port for Tor target. # Node2, no -bind=...=onion, thus no extra port for Tor target.
port = extra_port()
self.expected.append( self.expected.append(
[ [
[f"-bind=127.0.0.1:{port}"], [f"-bind=127.0.0.1:{port}"],
[(loopback_ipv4, port)] [(loopback_ipv4, port)],
], ],
) )
port += 1
self.extra_args = list(map(lambda e: e[0], self.expected)) self.extra_args = list(map(lambda e: e[0], self.expected))
self.setup_nodes() self.setup_nodes()

View File

@ -100,17 +100,5 @@ class DataCarrierTest(BitcoinTestFramework):
self.test_null_data_transaction(node=self.nodes[2], data=one_byte, success=True) self.test_null_data_transaction(node=self.nodes[2], data=one_byte, success=True)
self.test_null_data_transaction(node=self.nodes[3], data=one_byte, success=False) self.test_null_data_transaction(node=self.nodes[3], data=one_byte, success=False)
# Clean shutdown boilerplate due to deprecation
self.expected_stderr = [
"", # node 0 has no deprecated options
"Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.",
"Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.",
"Warning: Options '-datacarrier' or '-datacarriersize' are set but are marked as deprecated and are expected to be removed in a future version.",
]
for i in range(self.num_nodes):
self.stop_node(i, expected_stderr=self.expected_stderr[i])
if __name__ == '__main__': if __name__ == '__main__':
DataCarrierTest(__file__).main() DataCarrierTest(__file__).main()

View File

@ -165,23 +165,36 @@ class MempoolTRUC(BitcoinTestFramework):
def test_truc_reorg(self): def test_truc_reorg(self):
node = self.nodes[0] node = self.nodes[0]
self.log.info("Test that, during a reorg, TRUC rules are not enforced") self.log.info("Test that, during a reorg, TRUC rules are not enforced")
tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2) self.check_mempool([])
tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3)
tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3) # Testing 2<-3 versions allowed
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"]]) tx_v2_block = self.wallet.create_self_transfer(version=2)
# Testing 3<-2 versions allowed
tx_v3_block = self.wallet.create_self_transfer(version=3)
# Testing overly-large child size
tx_v3_block2 = self.wallet.create_self_transfer(version=3)
# Also create a linear chain of 3 TRUC transactions that will be directly mined, followed by one v2 in-mempool after block is made
tx_chain_1 = self.wallet.create_self_transfer(version=3)
tx_chain_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_1["new_utxo"], version=3)
tx_chain_3 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_2["new_utxo"], version=3)
tx_to_mine = [tx_v3_block["hex"], tx_v2_block["hex"], tx_v3_block2["hex"], tx_chain_1["hex"], tx_chain_2["hex"], tx_chain_3["hex"]]
block = self.generateblock(node, output="raw(42)", transactions=tx_to_mine)
block = self.generate(node, 1)
self.check_mempool([]) self.check_mempool([])
tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2) tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2)
tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3) tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3)
tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_vsize=1250, version=3) tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_vsize=1250, version=3)
assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], TRUC_CHILD_MAX_VSIZE) assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], TRUC_CHILD_MAX_VSIZE)
self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]]) tx_chain_4 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_chain_3["new_utxo"], version=2)
node.invalidateblock(block[0]) self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_4["txid"]])
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
# This is needed because generate() will create the exact same block again.
node.reconsiderblock(block[0])
# Reorg should have all block transactions re-accepted, ignoring TRUC enforcement
node.invalidateblock(block["hash"])
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_1["txid"], tx_chain_2["txid"], tx_chain_3["txid"], tx_chain_4["txid"]])
@cleanup(extra_args=["-limitdescendantsize=10"]) @cleanup(extra_args=["-limitdescendantsize=10"])
def test_nondefault_package_limits(self): def test_nondefault_package_limits(self):

View File

@ -15,7 +15,7 @@ from test_framework.wallet import MiniWallet
import time import time
class P2PNode(P2PDataStore): class P2PNode(P2PDataStore):
def on_inv(self, msg): def on_inv(self, message):
pass pass
@ -26,6 +26,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
self.gen_node = self.nodes[0] # The block and tx generating node self.gen_node = self.nodes[0] # The block and tx generating node
self.miniwallet = MiniWallet(self.gen_node) self.miniwallet = MiniWallet(self.gen_node)
self.mocktime = int(time.time())
self.test_tx_in_block() self.test_tx_in_block()
self.test_notfound_on_replaced_tx() self.test_notfound_on_replaced_tx()
@ -33,20 +34,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
def test_tx_in_block(self): def test_tx_in_block(self):
self.log.info("Check that a transaction in the last block is uploaded (beneficial for compact block relay)") self.log.info("Check that a transaction in the last block is uploaded (beneficial for compact block relay)")
self.gen_node.setmocktime(self.mocktime)
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) inbound_peer = self.gen_node.add_p2p_connection(P2PNode())
self.log.debug("Generate transaction and block") self.log.debug("Generate transaction and block")
inbound_peer.last_message.pop("inv", None) inbound_peer.last_message.pop("inv", None)
self.gen_node.setmocktime(int(time.time())) # pause time based activities
wtxid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"] wtxid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
rawmp = self.gen_node.getrawmempool(False, True) rawmp = self.gen_node.getrawmempool(False, True)
pi = self.gen_node.getpeerinfo()[0] pi = self.gen_node.getpeerinfo()[0]
assert_equal(rawmp["mempool_sequence"], 2) # our tx cause mempool activity assert_equal(rawmp["mempool_sequence"], 2) # our tx cause mempool activity
assert_equal(pi["last_inv_sequence"], 1) # that is after the last inv assert_equal(pi["last_inv_sequence"], 1) # that is after the last inv
assert_equal(pi["inv_to_send"], 1) # and our tx has been queued assert_equal(pi["inv_to_send"], 1) # and our tx has been queued
self.gen_node.setmocktime(0) self.mocktime += 120
self.gen_node.setmocktime(self.mocktime)
inbound_peer.wait_until(lambda: "inv" in inbound_peer.last_message and inbound_peer.last_message.get("inv").inv[0].hash == int(wtxid, 16)) inbound_peer.wait_until(lambda: "inv" in inbound_peer.last_message and inbound_peer.last_message.get("inv").inv[0].hash == int(wtxid, 16))
rawmp = self.gen_node.getrawmempool(False, True) rawmp = self.gen_node.getrawmempool(False, True)
@ -65,15 +66,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
def test_notfound_on_replaced_tx(self): def test_notfound_on_replaced_tx(self):
self.gen_node.disconnect_p2ps() self.gen_node.disconnect_p2ps()
self.gen_node.setmocktime(self.mocktime)
inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore()) inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore())
self.log.info("Transaction tx_a is broadcast") self.log.info("Transaction tx_a is broadcast")
tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node) tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node)
self.mocktime += 120
self.gen_node.setmocktime(self.mocktime)
inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]]) inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]])
tx_b = tx_a["tx"] tx_b = tx_a["tx"]
tx_b.vout[0].nValue -= 9000 tx_b.vout[0].nValue -= 9000
self.gen_node.sendrawtransaction(tx_b.serialize().hex()) self.gen_node.sendrawtransaction(tx_b.serialize().hex())
self.mocktime += 120
self.gen_node.setmocktime(self.mocktime)
inbound_peer.wait_until(lambda: "tx" in inbound_peer.last_message and inbound_peer.last_message.get("tx").tx.wtxid_hex == tx_b.wtxid_hex) inbound_peer.wait_until(lambda: "tx" in inbound_peer.last_message and inbound_peer.last_message.get("tx").tx.wtxid_hex == tx_b.wtxid_hex)
self.log.info("Re-request of tx_a after replacement is answered with notfound") self.log.info("Re-request of tx_a after replacement is answered with notfound")
@ -96,28 +102,31 @@ class P2PLeakTxTest(BitcoinTestFramework):
self.gen_node.disconnect_p2ps() self.gen_node.disconnect_p2ps()
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer
MAX_REPEATS = 100 # Set a mock time so that time does not pass, and gen_node never announces the transaction
self.log.info("Running test up to {} times.".format(MAX_REPEATS)) self.gen_node.setmocktime(self.mocktime)
for i in range(MAX_REPEATS): wtxid = int(self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"], 16)
self.log.info('Run repeat {}'.format(i + 1))
txid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
want_tx = msg_getdata() want_tx = msg_getdata()
want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16))) want_tx.inv.append(CInv(t=MSG_WTX, h=wtxid))
with p2p_lock: with p2p_lock:
inbound_peer.last_message.pop('notfound', None) inbound_peer.last_message.pop('notfound', None)
inbound_peer.send_and_ping(want_tx) inbound_peer.send_and_ping(want_tx)
inbound_peer.wait_until(lambda: "notfound" in inbound_peer.last_message)
with p2p_lock:
assert_equal(inbound_peer.last_message.get("notfound").vec[0].hash, wtxid)
inbound_peer.last_message.pop('notfound')
if inbound_peer.last_message.get('notfound'): # Move mocktime forward and wait for the announcement.
self.log.debug('tx {} was not yet announced to us.'.format(txid)) inbound_peer.last_message.pop('inv', None)
self.log.debug("node has responded with a notfound message. End test.") self.mocktime += 120
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16)) self.gen_node.setmocktime(self.mocktime)
with p2p_lock: inbound_peer.wait_for_inv([CInv(t=MSG_WTX, h=wtxid)], timeout=120)
inbound_peer.last_message.pop('notfound')
break # Send the getdata again, this time the node should send us a TX message.
else: inbound_peer.last_message.pop('tx', None)
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid)) inbound_peer.send_and_ping(want_tx)
assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv] self.wait_until(lambda: "tx" in inbound_peer.last_message)
assert_equal(wtxid, int(inbound_peer.last_message["tx"].tx.wtxid_hex, 16))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -213,6 +213,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(gdi_result, { assert_equal(gdi_result, {
"hash": blockhash, "hash": blockhash,
"height": height, "height": height,
"script_flags": ["CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DERSIG","NULLDUMMY","P2SH","TAPROOT","WITNESS"],
"deployments": { "deployments": {
'bip34': {'type': 'buried', 'active': True, 'height': 2}, 'bip34': {'type': 'buried', 'active': True, 'height': 2},
'bip66': {'type': 'buried', 'active': True, 'height': 3}, 'bip66': {'type': 'buried', 'active': True, 'height': 3},

View File

@ -474,6 +474,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect=
# min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind = 151 + 8 + 3 = 162; # min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind = 151 + 8 + 3 = 162;
# nMaxConnections = available_fds - min_required_fds = 256 - 161 = 94; # nMaxConnections = available_fds - min_required_fds = 256 - 161 = 94;
f.write("maxconnections=94\n") f.write("maxconnections=94\n")
f.write("par=" + str(min(2, os.cpu_count())) + "\n")
f.write(extra_config) f.write(extra_config)