dash/test
pasta 2f93ee4a53
Merge #6048: backport: merge bitcoin#19776, #20599, #22147, #22340, #20799, #25147, #20764, bitcoin-core/gui#206 (BIP152 backports)
1cbf3b9a53 merge bitcoin-core/gui#206: Display fRelayTxes and bip152_highbandwidth_{to, from} in peer details (Kittywhiskers Van Gogh)
239062192e merge bitcoin#20764: cli -netinfo peer connections dashboard updates (Kittywhiskers Van Gogh)
06a6f8444c merge bitcoin#25147: follow ups to #20799 (removing support for v1 compact blocks) (Kittywhiskers Van Gogh)
6274a571b7 merge bitcoin#20799: Only support version 2 compact blocks (Kittywhiskers Van Gogh)
f4ce573538 merge bitcoin#22340: Use legacy relaying to download blocks in blocks-only mode (Kittywhiskers Van Gogh)
73b8f84fdb merge bitcoin#22147: p2p: Protect last outbound HB compact block peer (Kittywhiskers Van Gogh)
2ce481849a merge bitcoin#20599: Tolerate sendheaders and sendcmpct messages before verack (Kittywhiskers Van Gogh)
799214b2c8 merge bitcoin#19776: expose high bandwidth mode state via getpeerinfo (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * Version 2 of BIP152 high-bandwidth mode/compact blocks implements SegWit support.

    As Dash does not implement SegWit, there has never been a need to implement v2 (and therefore, have all the code necessary to support both v1 and v2, that gets removed as part of making support v2 only).

    * Despite that, the changes surrounding removing support for both versions (that in our case, do not apply as we never have supported v2) refactor the code in other ways and influence their behaviour. In the interest of upstream alignment, those changes have been backported.

  * [bitcoin#19776](https://github.com/bitcoin/bitcoin/pull/19776) doesn't seem to work on its own without successive backports, specifically [bitcoin#20799](https://github.com/bitcoin/bitcoin/pull/20799), despite the latter being a later backport.

    <details>

    <summary>19776-only p2p_compactblocks.py run (9f2c868947cc254d021e1a9bd00eb7bc80061e81)</summary>

    ```
    dash@825a14c32b73:/src/dash$ ./test/functional/p2p_compactblocks.py
    2024-06-09T12:29:09.777000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_kb2nr5oe
    2024-06-09T12:29:16.341000Z TestFramework (INFO): Testing SENDCMPCT p2p message...
    2024-06-09T12:29:31.432000Z TestFramework (INFO): Testing compactblock construction...
    2024-06-09T12:29:40.068000Z TestFramework (INFO): Testing compactblock requests...
    2024-06-09T12:29:44.597000Z TestFramework (INFO): Testing getblocktxn handler...
    2024-06-09T12:29:59.808000Z TestFramework (INFO): Testing compactblock requests/announcements not at chain tip...
    2024-06-09T12:30:03.855000Z TestFramework (INFO): Testing handling of incorrect blocktxn responses...
    2024-06-09T12:30:05.868000Z TestFramework (INFO): Testing reconstructing compact blocks from all peers...
    2024-06-09T12:30:09.389000Z TestFramework (INFO): Testing end-to-end block relay...
    2024-06-09T12:30:10.404000Z TestFramework (INFO): Testing handling of invalid compact blocks...
    2024-06-09T12:30:12.418000Z TestFramework (INFO): Testing invalid index in cmpctblock message...
    2024-06-09T12:30:14.384000Z TestFramework (INFO): Testing high-bandwidth mode states via getpeerinfo...
    2024-06-09T12:30:16.893000Z TestFramework (ERROR): Assertion failed
    Traceback (most recent call last):
      File "/src/dash/test/functional/test_framework/test_framework.py", line 158, in main
        self.run_test()
      File "./test/functional/p2p_compactblocks.py", line 849, in run_test
        self.test_highbandwidth_mode_states_via_getpeerinfo()
      File "./test/functional/p2p_compactblocks.py", line 791, in test_highbandwidth_mode_states_via_getpeerinfo
        hb_test_node.send_and_ping(msg_block(block))
      File "/src/dash/test/functional/test_framework/p2p.py", line 579, in send_and_ping
        self.sync_with_ping(timeout=timeout)
      File "/src/dash/test/functional/test_framework/p2p.py", line 596, in sync_with_ping
        self.wait_until(test_function, timeout=timeout)
      File "/src/dash/test/functional/test_framework/p2p.py", line 487, in wait_until
        wait_until_helper(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
      File "/src/dash/test/functional/test_framework/util.py", line 249, in wait_until_helper
        if predicate():
      File "/src/dash/test/functional/test_framework/p2p.py", line 484, in test_function
        assert self.is_connected
    AssertionError
    2024-06-09T12:30:17.396000Z TestFramework (INFO): Stopping nodes
    2024-06-09T12:30:18.400000Z TestFramework (WARNING): Not cleaning up dir /tmp/dash_func_test_kb2nr5oe
    2024-06-09T12:30:18.401000Z TestFramework (ERROR): Test failed. Test logging available at /tmp/dash_func_test_kb2nr5oe/test_framework.log
    2024-06-09T12:30:18.401000Z TestFramework (ERROR):
    2024-06-09T12:30:18.401000Z TestFramework (ERROR): Hint: Call /src/dash/test/functional/combine_logs.py '/tmp/dash_func_test_kb2nr5oe' to consolidate all logs
    2024-06-09T12:30:18.401000Z TestFramework (ERROR):
    2024-06-09T12:30:18.401000Z TestFramework (ERROR): If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.
    2024-06-09T12:30:18.402000Z TestFramework (ERROR): https://github.com/dashpay/dash/issues
    2024-06-09T12:30:18.402000Z TestFramework (ERROR):
    ```

    </details>

    <details>

    <summary>20799-incl p2p_compactblocks.py run (aa116c4f0b4753b615f9483aa03adec5ee4fd655)</summary>

    ```
    dash@825a14c32b73:/src/dash$ ./test/functional/p2p_compactblocks.py
    2024-06-09T12:34:27.169000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_7d65lmhz
    2024-06-09T12:34:32.695000Z TestFramework (INFO): Testing SENDCMPCT p2p message...
    2024-06-09T12:34:51.288000Z TestFramework (INFO): Testing compactblock construction...
    2024-06-09T12:34:55.325000Z TestFramework (INFO): Testing compactblock requests...
    2024-06-09T12:34:59.861000Z TestFramework (INFO): Testing getblocktxn handler...
    2024-06-09T12:35:07.460000Z TestFramework (INFO): Testing compactblock requests/announcements not at chain tip...
    2024-06-09T12:35:09.503000Z TestFramework (INFO): Testing handling of incorrect blocktxn responses...
    2024-06-09T12:35:11.519000Z TestFramework (INFO): Testing reconstructing compact blocks from all peers...
    2024-06-09T12:35:15.039000Z TestFramework (INFO): Testing end-to-end block relay...
    2024-06-09T12:35:16.055000Z TestFramework (INFO): Testing handling of invalid compact blocks...
    2024-06-09T12:35:17.062000Z TestFramework (INFO): Testing invalid index in cmpctblock message...
    2024-06-09T12:35:19.139000Z TestFramework (INFO): Testing high-bandwidth mode states via getpeerinfo...
    2024-06-09T12:35:22.159000Z TestFramework (INFO): Stopping nodes
    2024-06-09T12:35:23.163000Z TestFramework (INFO): Cleaning up /tmp/dash_func_test_7d65lmhz on exit
    2024-06-09T12:35:23.163000Z TestFramework (INFO): Tests successful
    ```
    </details>

  * The backport of [bitcoin-core/gui#206](https://github.com/bitcoin-core/gui/pull/206) is a continuation of 3e8ba24c87 from [dash#5964](https://github.com/dashpay/dash/pull/5964)

  * The backport of [bitcoin#20764](https://github.com/bitcoin/bitcoin/pull/20764) is a continuation of bd934c71eb from [dash#6034](https://github.com/dashpay/dash/pull/6034)

  ## Breaking changes

  * The `getpeerinfo` RPC returns two new boolean fields, `bip152_hb_to` and `bip152_hb_from`, that respectively indicate whether we selected a peer to be in compact blocks high-bandwidth mode or whether a peer selected us as a compact blocks high-bandwidth peer.

    High-bandwidth peers send new block announcements via a `cmpctblock` message rather than the usual inv/headers announcements. See BIP 152 for more details.

  * Blocks-only mode will use legacy relaying instead of BIP152 high-bandwidth mode

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)**
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK 1cbf3b9a53
  PastaPastaPasta:
    utACK 1cbf3b9a53
  knst:
    utACK 1cbf3b9a53

Tree-SHA512: 5947b622d8d57a1dc9445cd6e07d4ad690379416d0fcf04ed574269975d1beb704691a79ff081341f3c800cf11869d401f1ed90baa5449f371f9ce658f2d2e95
2024-06-11 08:42:46 -05:00
..
functional Merge #6048: backport: merge bitcoin#19776, #20599, #22147, #22340, #20799, #25147, #20764, bitcoin-core/gui#206 (BIP152 backports) 2024-06-11 08:42:46 -05:00
fuzz Merge #18673: scripted-diff: Sort test includes 2023-08-29 22:00:59 -05:00
lint fix: disable linter 'check-rpc-mapping.py' for composite commands 2024-06-10 17:29:13 +07:00
sanitizer_suppressions Merge #19983: Drop some TSan suppressions 2024-03-27 00:48:28 +07:00
util refactor: use better masks for list of files; add missing bip39.{h,cpp} 2024-03-24 00:41:23 +07:00
config.ini.in
get_previous_releases.py fix: follow-up partial bitcoin/bitcoin#25063 - actually load binaries with x86_64-apple-darwin platform 2023-11-24 11:23:46 -06:00
README.md Merge bitcoin/bitcoin#21740: test: add new python linter to check file names and permissions 2024-05-16 02:09:37 +07:00

This directory contains integration tests that test dashd and its utilities in their entirety. It does not contain unit tests, which can be found in /src/test, /src/wallet/test, etc.

This directory contains the following sets of tests:

  • functional which test the functionality of dashd and dash-qt by interacting with them through the RPC and P2P interfaces.
  • util which tests the dash utilities, currently only dash-tx.
  • lint which perform various static analysis checks.

The util tests are run as part of make check target. The functional tests and lint scripts can be run as explained in the sections below.

Running tests locally

Before tests can be run locally, Dash Core must be built. See the building instructions for help.

Functional tests

Dependencies and prerequisites

Many Dash specific tests require dash_hash. To install it:

  • Clone the repo git clone https://github.com/dashpay/dash_hash
  • Install dash_hash cd dash_hash && pip3 install -r requirements.txt .

The ZMQ functional test requires a python ZMQ library. To install it:

  • on Unix, run sudo apt-get install python3-zmq
  • on mac OS, run pip3 install pyzmq

On Windows the PYTHONUTF8 environment variable must be set to 1:

set PYTHONUTF8=1

Running the tests

Individual tests can be run by directly calling the test script, e.g.:

test/functional/wallet_hd.py

or can be run through the test_runner harness, eg:

test/functional/test_runner.py wallet_hd.py

You can run any combination (incl. duplicates) of tests by calling:

test/functional/test_runner.py <testname1> <testname2> <testname3> ...

Wildcard test names can be passed, if the paths are coherent and the test runner is called from a bash shell or similar that does the globbing. For example, to run all the wallet tests:

test/functional/test_runner.py test/functional/wallet*
functional/test_runner.py functional/wallet* (called from the test/ directory)
test_runner.py wallet* (called from the test/functional/ directory)

but not

test/functional/test_runner.py wallet*

Combinations of wildcards can be passed:

test/functional/test_runner.py ./test/functional/tool* test/functional/mempool*
test_runner.py tool* mempool*

Run the regression test suite with:

test/functional/test_runner.py

Run all possible tests with

test/functional/test_runner.py --extended

In order to run backwards compatibility tests, download the previous node binaries:

test/get_previous_releases.py -b v19.3.0 v18.2.2 v0.17.0.3 v0.16.1.1 v0.15.0.0

By default, up to 4 tests will be run in parallel by test_runner. To specify how many jobs to run, append --jobs=n

The individual tests and the test_runner harness have many command-line options. Run test/functional/test_runner.py -h to see them all.

Speed up test runs with a ramdisk

If you have available RAM on your system you can create a ramdisk to use as the cache and tmp directories for the functional tests in order to speed them up. Speed-up amount varies on each system (and according to your ram speed and other variables), but a 2-3x speed-up is not uncommon.

To create a 4GB ramdisk on Linux at /mnt/tmp/:

sudo mkdir -p /mnt/tmp
sudo mount -t tmpfs -o size=4g tmpfs /mnt/tmp/

Configure the size of the ramdisk using the size= option. The size of the ramdisk needed is relative to the number of concurrent jobs the test suite runs. For example running the test suite with --jobs=100 might need a 16GB ramdisk, but running with --jobs=4 will only need a 4GB ramdisk.

To use, run the test suite specifying the ramdisk as the cachedir and tmpdir:

test/functional/test_runner.py --cachedir=/mnt/tmp/cache --tmpdir=/mnt/tmp

Once finished with the tests and the disk, and to free the ram, simply unmount the disk:

sudo umount /mnt/tmp

Troubleshooting and debugging test failures

Resource contention

The P2P and RPC ports used by the dashd nodes-under-test are chosen to make conflicts with other processes unlikely. However, if there is another dashd process running on the system (perhaps from a previous test which hasn't successfully killed all its dashd nodes), then there may be a port conflict which will cause the test to fail. It is recommended that you run the tests on a system where no other dashd processes are running.

On linux, the test framework will warn if there is another dashd process running when the tests are started.

If there are zombie dashd processes after test failure, you can kill them by running the following commands. Note that these commands will kill all dashd processes running on the system, so should not be used if any non-test dashd processes are being run.

killall dashd

or

pkill -9 dashd
Data directory cache

A pre-mined blockchain with 200 blocks is generated the first time a functional test is run and is stored in test/cache. This speeds up test startup times since new blockchains don't need to be generated for each test. However, the cache may get into a bad state, in which case tests will fail. If this happens, remove the cache directory (and make sure dashd processes are stopped as above):

rm -rf test/cache
killall dashd
Test logging

The tests contain logging at five different levels (DEBUG, INFO, WARNING, ERROR and CRITICAL). From within your functional tests you can log to these different levels using the logger included in the test_framework, e.g. self.log.debug(object). By default:

  • when run through the test_runner harness, all logs are written to test_framework.log and no logs are output to the console.
  • when run directly, all logs are written to test_framework.log and INFO level and above are output to the console.
  • when run by our CI (Continuous Integration), no logs are output to the console. However, if a test fails, the test_framework.log and dashd debug.logs will all be dumped to the console to help troubleshooting.

These log files can be located under the test data directory (which is always printed in the first line of test output):

  • <test data directory>/test_framework.log
  • <test data directory>/node<node number>/regtest/debug.log.

The node number identifies the relevant test node, starting from node0, which corresponds to its position in the nodes list of the specific test, e.g. self.nodes[0].

To change the level of logs output to the console, use the -l command line argument.

test_framework.log and dashd debug.logs can be combined into a single aggregate log by running the combine_logs.py script. The output can be plain text, colorized text or html. For example:

test/functional/combine_logs.py -c <test data directory> | less -r

will pipe the colorized logs from the test into less.

Use --tracerpc to trace out all the RPC calls and responses to the console. For some tests (eg any that use submitblock to submit a full block over RPC), this can result in a lot of screen output.

By default, the test data directory will be deleted after a successful run. Use --nocleanup to leave the test data directory intact. The test data directory is never deleted after a failed test.

Attaching a debugger

A python debugger can be attached to tests at any point. Just add the line:

import pdb; pdb.set_trace()

anywhere in the test. You will then be able to inspect variables, as well as call methods that interact with the dashd nodes-under-test.

If further introspection of the dashd instances themselves becomes necessary, this can be accomplished by first setting a pdb breakpoint at an appropriate location, running the test to that point, then using gdb (or lldb on macOS) to attach to the process and debug.

For instance, to attach to self.node[1] during a run you can get the pid of the node within pdb.

(pdb) self.node[1].process.pid

Alternatively, you can find the pid by inspecting the temp folder for the specific test you are running. The path to that folder is printed at the beginning of every test run:

2017-06-27 14:13:56.686000 TestFramework (INFO): Initializing test directory /tmp/user/1000/testo9vsdjo3

Use the path to find the pid file in the temp folder:

cat /tmp/user/1000/testo9vsdjo3/node1/regtest/dashd.pid

Then you can use the pid to start gdb:

gdb /home/example/dashd <pid>

Note: gdb attach step may require ptrace_scope to be modified, or sudo preceding the gdb. See this link for considerations: https://www.kernel.org/doc/Documentation/security/Yama.txt

Often while debugging rpc calls from functional tests, the test might reach timeout before process can return a response. Use --timeout-factor 0 to disable all rpc timeouts for that partcular functional test. Ex: test/functional/wallet_hd.py --timeout-factor 0.

Profiling

An easy way to profile node performance during functional tests is provided for Linux platforms using perf.

Perf will sample the running node and will generate profile data in the node's datadir. The profile data can then be presented using perf report or a graphical tool like hotspot.

To generate a profile during test suite runs, use the --perf flag.

To see render the output to text, run

perf report -i /path/to/datadir/send-big-msgs.perf.data.xxxx --stdio | c++filt | less

For ways to generate more granular profiles, see the README in test/functional.

Util tests

Util tests can be run locally by running test/util/bitcoin-util-test.py. Use the -v option for verbose output.

Lint tests

Dependencies

Lint test Dependency Version used by CI Installation
lint-python.sh flake8 3.8.3 pip3 install flake8==3.8.3
lint-python.sh mypy 0.781 pip3 install mypy==0.781
lint-shell.sh ShellCheck 0.7.2 details...
lint-shell.sh yq default pip3 install yq
lint-spelling.sh codespell 2.0.0 pip3 install codespell==2.0.0

Please be aware that on Linux distributions all dependencies are usually available as packages, but could be outdated.

Running the tests

Individual tests can be run by directly calling the test script, e.g.:

test/lint/lint-files.sh

You can run all the shell-based lint tests by running:

test/lint/lint-all.sh

Writing functional tests

You are encouraged to write functional tests for new or existing features. Further information about the functional test framework and individual tests is found in test/functional.