Page MenuHomePhabricator

Drop miniupnp dependency
ClosedPublic

Authored by PiRK on Mar 31 2026, 14:38.

Details

Reviewers
Fabien
Group Reviewers
Restricted Project
Maniphest Tasks
Restricted Maniphest Task
Commits
rABC012f53033ca6: Drop miniupnp dependency
Summary

Miniupnpc is a C library (somewhat) maintained by a single person which had several vulnerabilities in the past, some of which directly affected our software.

The main purpose of this functionality is to have more (non-data-center) reachable nodes on the network. For a non-technical user running Bitcoin ABC at home, the software would automatically open a port on their router to receive incoming connections. This way, users not able to manually open a port on their router would still provide the network with more resources and enhance its diversity.

However, due to past vulnerabilities (and a worry about unknown future ones) in miniupnpc this feature was disabled by default in core#6795. Having it disabled by default kills (most of?) the purpose of having this functionality in the first place: someone technical enough to understand the -upnp startup option or the "enable UPnP" setting is most likely able to open a port on his box in the first place.

In addition, laanwj implemented PCP with a NAT-PMP fallback. If we ever want to re-enable automatic NAT traversal by default in Bitcoin ABC, this is the best option. The NAT-PMP fallback makes it so compatibility shouldn't be (much of) an issue.

This is a backport of core#31130
Depends on D19778
Ref T3982

Test Plan

ninja all check-all
guix builds

Diff Detail

Repository
rABC Bitcoin ABC
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

Tail of the build log:

PASSED                     [ 35%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.299' duration='46' flowId='tests.test_iguana.test_script_sig_success' name='tests.test_iguana.test_script_sig_success']

tests/test_iguana.py::test_script_sig_invalid_opcode_encoding ##teamcity[testStarted timestamp='2026-03-31T15:15:46.300' captureStandardOutput='false' flowId='tests.test_iguana.test_script_sig_invalid_opcode_encoding' metainfo='test_script_sig_invalid_opcode_encoding' name='tests.test_iguana.test_script_sig_invalid_opcode_encoding']
PASSED     [ 40%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.305' duration='4' flowId='tests.test_iguana.test_script_sig_invalid_opcode_encoding' name='tests.test_iguana.test_script_sig_invalid_opcode_encoding']

tests/test_iguana.py::test_script_pub_key_success ##teamcity[testStarted timestamp='2026-03-31T15:15:46.306' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_success' metainfo='test_script_pub_key_success' name='tests.test_iguana.test_script_pub_key_success']
PASSED                 [ 45%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.311' duration='4' flowId='tests.test_iguana.test_script_pub_key_success' name='tests.test_iguana.test_script_pub_key_success']

tests/test_iguana.py::test_script_pub_key_failure ##teamcity[testStarted timestamp='2026-03-31T15:15:46.312' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_failure' metainfo='test_script_pub_key_failure' name='tests.test_iguana.test_script_pub_key_failure']
PASSED                 [ 50%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.315' duration='2' flowId='tests.test_iguana.test_script_pub_key_failure' name='tests.test_iguana.test_script_pub_key_failure']

tests/test_iguana.py::test_script_pub_key_empty_stack ##teamcity[testStarted timestamp='2026-03-31T15:15:46.315' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_empty_stack' metainfo='test_script_pub_key_empty_stack' name='tests.test_iguana.test_script_pub_key_empty_stack']
PASSED             [ 55%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.318' duration='2' flowId='tests.test_iguana.test_script_pub_key_empty_stack' name='tests.test_iguana.test_script_pub_key_empty_stack']

tests/test_iguana.py::test_script_pub_key_false_stack ##teamcity[testStarted timestamp='2026-03-31T15:15:46.319' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_false_stack' metainfo='test_script_pub_key_false_stack' name='tests.test_iguana.test_script_pub_key_false_stack']
PASSED             [ 60%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.322' duration='2' flowId='tests.test_iguana.test_script_pub_key_false_stack' name='tests.test_iguana.test_script_pub_key_false_stack']

tests/test_iguana.py::test_script_pub_key_cleanstack ##teamcity[testStarted timestamp='2026-03-31T15:15:46.323' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_cleanstack' metainfo='test_script_pub_key_cleanstack' name='tests.test_iguana.test_script_pub_key_cleanstack']
PASSED              [ 65%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.326' duration='2' flowId='tests.test_iguana.test_script_pub_key_cleanstack' name='tests.test_iguana.test_script_pub_key_cleanstack']

tests/test_iguana.py::test_redeem_script_success ##teamcity[testStarted timestamp='2026-03-31T15:15:46.327' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_success' metainfo='test_redeem_script_success' name='tests.test_iguana.test_redeem_script_success']
PASSED                  [ 70%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.333' duration='6' flowId='tests.test_iguana.test_redeem_script_success' name='tests.test_iguana.test_redeem_script_success']

tests/test_iguana.py::test_redeem_script_error ##teamcity[testStarted timestamp='2026-03-31T15:15:46.334' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_error' metainfo='test_redeem_script_error' name='tests.test_iguana.test_redeem_script_error']
PASSED                    [ 75%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.337' duration='2' flowId='tests.test_iguana.test_redeem_script_error' name='tests.test_iguana.test_redeem_script_error']

tests/test_iguana.py::test_redeem_script_exception ##teamcity[testStarted timestamp='2026-03-31T15:15:46.338' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_exception' metainfo='test_redeem_script_exception' name='tests.test_iguana.test_redeem_script_exception']
PASSED                [ 80%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.342' duration='3' flowId='tests.test_iguana.test_redeem_script_exception' name='tests.test_iguana.test_redeem_script_exception']

tests/test_iguana.py::test_redeem_script_empty_stack ##teamcity[testStarted timestamp='2026-03-31T15:15:46.343' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_empty_stack' metainfo='test_redeem_script_empty_stack' name='tests.test_iguana.test_redeem_script_empty_stack']
PASSED              [ 85%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.357' duration='14' flowId='tests.test_iguana.test_redeem_script_empty_stack' name='tests.test_iguana.test_redeem_script_empty_stack']

tests/test_iguana.py::test_redeem_script_false ##teamcity[testStarted timestamp='2026-03-31T15:15:46.358' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_false' metainfo='test_redeem_script_false' name='tests.test_iguana.test_redeem_script_false']
PASSED                    [ 90%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.373' duration='14' flowId='tests.test_iguana.test_redeem_script_false' name='tests.test_iguana.test_redeem_script_false']

tests/test_iguana.py::test_redeem_script_cleanstack ##teamcity[testStarted timestamp='2026-03-31T15:15:46.373' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_cleanstack' metainfo='test_redeem_script_cleanstack' name='tests.test_iguana.test_redeem_script_cleanstack']
PASSED               [ 95%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.379' duration='5' flowId='tests.test_iguana.test_redeem_script_cleanstack' name='tests.test_iguana.test_redeem_script_cleanstack']

tests/test_iguana.py::test_redeem_script_input_sigchecks ##teamcity[testStarted timestamp='2026-03-31T15:15:46.379' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_input_sigchecks' metainfo='test_redeem_script_input_sigchecks' name='tests.test_iguana.test_redeem_script_input_sigchecks']
PASSED          [100%]##teamcity[testFinished timestamp='2026-03-31T15:15:46.399' duration='19' flowId='tests.test_iguana.test_redeem_script_input_sigchecks' name='tests.test_iguana.test_redeem_script_input_sigchecks']


============================== 20 passed in 0.36s ==============================
[190/504] Building CXX object src/test/CMakeFiles/test_bitcoin.dir/pcp_tests.cpp.o
FAILED: src/test/CMakeFiles/test_bitcoin.dir/pcp_tests.cpp.o 
/usr/bin/ccache /usr/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_NO_CXX98_FUNCTION_BASE -DBUILD_BITCOIN_INTERNAL -DENABLE_AVX2 -DENABLE_SHANI -DENABLE_SSE41 -DHAVE_BUILD_INFO -DHAVE_CONFIG_H -DHAVE_CONSENSUS_LIB -I/work/src/univalue/include -I/work/src/. -I/work/abc-ci-builds/build-without-wallet/src -I/work/abc-ci-builds/build-without-wallet/src/crypto/.. -I/work/src/secp256k1/include -I/work/src/leveldb/include -I/work/src/crc32c/include -isystem /usr/include/jemalloc -Werror -g -O2 -fPIE -fvisibility=hidden -fstack-reuse=none -fstack-protector-all -Wstack-protector -fcf-protection=full -fstack-clash-protection -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wformat -Wvla -Wcast-align -Wunused-parameter -Wmissing-braces -Wno-implicit-fallthrough -Wno-psabi -Wno-unused-parameter -Wredundant-decls -Wsign-compare -Wduplicated-branches -Wduplicated-cond -Wlogical-op -Wformat-security -Wredundant-move -Wsuggest-override -Woverloaded-virtual -std=gnu++20 -MD -MT src/test/CMakeFiles/test_bitcoin.dir/pcp_tests.cpp.o -MF src/test/CMakeFiles/test_bitcoin.dir/pcp_tests.cpp.o.d -o src/test/CMakeFiles/test_bitcoin.dir/pcp_tests.cpp.o -c /work/src/test/pcp_tests.cpp
/work/src/test/pcp_tests.cpp:71:79: error: missing braces around initializer for ‘std::__array_traits<unsigned char, 12>::_Type’ {aka ‘unsigned char [12]’} [-Werror=missing-braces]
   71 |         0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};
      |                                                                               ^
cc1plus: all warnings being treated as errors
[220/504] Running seeder test suite
PASSED: seeder test suite
[226/504] Running pow test suite
PASSED: pow test suite
[232/504] Running avalanche test suite
PASSED: avalanche test suite
[239/504] Running bitcoin-qt test suite
PASSED: bitcoin-qt test suite
ninja: build stopped: cannot make progress due to previous errors.
Build build-without-wallet failed with exit code 1

Tail of the build log:

tests/test_iguana.py::test_invalid_inputindex ##teamcity[testStarted timestamp='2026-03-31T15:18:11.179' captureStandardOutput='false' flowId='tests.test_iguana.test_invalid_inputindex' metainfo='test_invalid_inputindex' name='tests.test_iguana.test_invalid_inputindex']
PASSED                     [ 25%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.198' duration='18' flowId='tests.test_iguana.test_invalid_inputindex' name='tests.test_iguana.test_invalid_inputindex']

tests/test_iguana.py::test_sig_push_only ##teamcity[testStarted timestamp='2026-03-31T15:18:11.199' captureStandardOutput='false' flowId='tests.test_iguana.test_sig_push_only' metainfo='test_sig_push_only' name='tests.test_iguana.test_sig_push_only']
PASSED                          [ 30%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.231' duration='31' flowId='tests.test_iguana.test_sig_push_only' name='tests.test_iguana.test_sig_push_only']

tests/test_iguana.py::test_script_sig_success ##teamcity[testStarted timestamp='2026-03-31T15:18:11.231' captureStandardOutput='false' flowId='tests.test_iguana.test_script_sig_success' metainfo='test_script_sig_success' name='tests.test_iguana.test_script_sig_success']
PASSED                     [ 35%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.290' duration='58' flowId='tests.test_iguana.test_script_sig_success' name='tests.test_iguana.test_script_sig_success']

tests/test_iguana.py::test_script_sig_invalid_opcode_encoding ##teamcity[testStarted timestamp='2026-03-31T15:18:11.291' captureStandardOutput='false' flowId='tests.test_iguana.test_script_sig_invalid_opcode_encoding' metainfo='test_script_sig_invalid_opcode_encoding' name='tests.test_iguana.test_script_sig_invalid_opcode_encoding']
PASSED     [ 40%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.326' duration='34' flowId='tests.test_iguana.test_script_sig_invalid_opcode_encoding' name='tests.test_iguana.test_script_sig_invalid_opcode_encoding']

tests/test_iguana.py::test_script_pub_key_success ##teamcity[testStarted timestamp='2026-03-31T15:18:11.327' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_success' metainfo='test_script_pub_key_success' name='tests.test_iguana.test_script_pub_key_success']
PASSED                 [ 45%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.390' duration='62' flowId='tests.test_iguana.test_script_pub_key_success' name='tests.test_iguana.test_script_pub_key_success']

tests/test_iguana.py::test_script_pub_key_failure ##teamcity[testStarted timestamp='2026-03-31T15:18:11.391' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_failure' metainfo='test_script_pub_key_failure' name='tests.test_iguana.test_script_pub_key_failure']
PASSED                 [ 50%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.419' duration='27' flowId='tests.test_iguana.test_script_pub_key_failure' name='tests.test_iguana.test_script_pub_key_failure']

tests/test_iguana.py::test_script_pub_key_empty_stack ##teamcity[testStarted timestamp='2026-03-31T15:18:11.419' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_empty_stack' metainfo='test_script_pub_key_empty_stack' name='tests.test_iguana.test_script_pub_key_empty_stack']
PASSED             [ 55%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.460' duration='39' flowId='tests.test_iguana.test_script_pub_key_empty_stack' name='tests.test_iguana.test_script_pub_key_empty_stack']

tests/test_iguana.py::test_script_pub_key_false_stack ##teamcity[testStarted timestamp='2026-03-31T15:18:11.461' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_false_stack' metainfo='test_script_pub_key_false_stack' name='tests.test_iguana.test_script_pub_key_false_stack']
PASSED             [ 60%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.472' duration='10' flowId='tests.test_iguana.test_script_pub_key_false_stack' name='tests.test_iguana.test_script_pub_key_false_stack']

tests/test_iguana.py::test_script_pub_key_cleanstack ##teamcity[testStarted timestamp='2026-03-31T15:18:11.473' captureStandardOutput='false' flowId='tests.test_iguana.test_script_pub_key_cleanstack' metainfo='test_script_pub_key_cleanstack' name='tests.test_iguana.test_script_pub_key_cleanstack']
PASSED              [ 65%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.495' duration='21' flowId='tests.test_iguana.test_script_pub_key_cleanstack' name='tests.test_iguana.test_script_pub_key_cleanstack']

tests/test_iguana.py::test_redeem_script_success ##teamcity[testStarted timestamp='2026-03-31T15:18:11.495' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_success' metainfo='test_redeem_script_success' name='tests.test_iguana.test_redeem_script_success']
PASSED                  [ 70%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.523' duration='26' flowId='tests.test_iguana.test_redeem_script_success' name='tests.test_iguana.test_redeem_script_success']

tests/test_iguana.py::test_redeem_script_error ##teamcity[testStarted timestamp='2026-03-31T15:18:11.524' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_error' metainfo='test_redeem_script_error' name='tests.test_iguana.test_redeem_script_error']
PASSED                    [ 75%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.535' duration='10' flowId='tests.test_iguana.test_redeem_script_error' name='tests.test_iguana.test_redeem_script_error']

tests/test_iguana.py::test_redeem_script_exception ##teamcity[testStarted timestamp='2026-03-31T15:18:11.536' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_exception' metainfo='test_redeem_script_exception' name='tests.test_iguana.test_redeem_script_exception']
PASSED                [ 80%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.549' duration='12' flowId='tests.test_iguana.test_redeem_script_exception' name='tests.test_iguana.test_redeem_script_exception']

tests/test_iguana.py::test_redeem_script_empty_stack ##teamcity[testStarted timestamp='2026-03-31T15:18:11.549' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_empty_stack' metainfo='test_redeem_script_empty_stack' name='tests.test_iguana.test_redeem_script_empty_stack']
PASSED              [ 85%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.556' duration='5' flowId='tests.test_iguana.test_redeem_script_empty_stack' name='tests.test_iguana.test_redeem_script_empty_stack']

tests/test_iguana.py::test_redeem_script_false ##teamcity[testStarted timestamp='2026-03-31T15:18:11.556' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_false' metainfo='test_redeem_script_false' name='tests.test_iguana.test_redeem_script_false']
PASSED                    [ 90%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.566' duration='9' flowId='tests.test_iguana.test_redeem_script_false' name='tests.test_iguana.test_redeem_script_false']

tests/test_iguana.py::test_redeem_script_cleanstack ##teamcity[testStarted timestamp='2026-03-31T15:18:11.567' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_cleanstack' metainfo='test_redeem_script_cleanstack' name='tests.test_iguana.test_redeem_script_cleanstack']
PASSED               [ 95%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.576' duration='8' flowId='tests.test_iguana.test_redeem_script_cleanstack' name='tests.test_iguana.test_redeem_script_cleanstack']

tests/test_iguana.py::test_redeem_script_input_sigchecks ##teamcity[testStarted timestamp='2026-03-31T15:18:11.577' captureStandardOutput='false' flowId='tests.test_iguana.test_redeem_script_input_sigchecks' metainfo='test_redeem_script_input_sigchecks' name='tests.test_iguana.test_redeem_script_input_sigchecks']
PASSED          [100%]##teamcity[testFinished timestamp='2026-03-31T15:18:11.603' duration='25' flowId='tests.test_iguana.test_redeem_script_input_sigchecks' name='tests.test_iguana.test_redeem_script_input_sigchecks']


============================== 20 passed in 0.57s ==============================
[237/544] Running seeder test suite
PASSED: seeder test suite
[251/544] Running pow test suite
PASSED: pow test suite
[256/544] Running bitcoin-qt test suite
PASSED: bitcoin-qt test suite
[261/544] Running avalanche test suite
PASSED: avalanche test suite
ninja: build stopped: cannot make progress due to previous errors.
Build build-debug failed with exit code 1

Tail of the build log:

wallet_watchonly.py --usecli                     | ✓ Passed  | 1 s
chronik_avalanche.py                             | ○ Skipped | 0 s
chronik_block.py                                 | ○ Skipped | 0 s
chronik_block_header.py                          | ○ Skipped | 0 s
chronik_block_info.py                            | ○ Skipped | 0 s
chronik_block_txs.py                             | ○ Skipped | 0 s
chronik_blockchain_info.py                       | ○ Skipped | 0 s
chronik_blocks.py                                | ○ Skipped | 0 s
chronik_broadcast_tx.py                          | ○ Skipped | 0 s
chronik_chronik_info.py                          | ○ Skipped | 0 s
chronik_cors.py                                  | ○ Skipped | 0 s
chronik_disable_token_index.py                   | ○ Skipped | 0 s
chronik_disallow_prune.py                        | ○ Skipped | 0 s
chronik_electrum_basic.py                        | ○ Skipped | 0 s
chronik_electrum_blockchain.py                   | ○ Skipped | 0 s
chronik_lokad_id_group.py                        | ○ Skipped | 0 s
chronik_mempool_conflicts.py                     | ○ Skipped | 0 s
chronik_mempool_disconnectpool.py                | ○ Skipped | 0 s
chronik_pause.py                                 | ○ Skipped | 0 s
chronik_plugin_groups.py                         | ○ Skipped | 0 s
chronik_plugins.py                               | ○ Skipped | 0 s
chronik_plugins_setup.py                         | ○ Skipped | 0 s
chronik_raw_tx.py                                | ○ Skipped | 0 s
chronik_resync.py                                | ○ Skipped | 0 s
chronik_script_confirmed_txs.py                  | ○ Skipped | 0 s
chronik_script_history.py                        | ○ Skipped | 0 s
chronik_script_unconfirmed_txs.py                | ○ Skipped | 0 s
chronik_script_utxos.py                          | ○ Skipped | 0 s
chronik_scripthash.py                            | ○ Skipped | 0 s
chronik_serve.py                                 | ○ Skipped | 0 s
chronik_shutdown.py                              | ○ Skipped | 0 s
chronik_spent_by.py                              | ○ Skipped | 0 s
chronik_token_alp.py                             | ○ Skipped | 0 s
chronik_token_broadcast_txs.py                   | ○ Skipped | 0 s
chronik_token_burn.py                            | ○ Skipped | 0 s
chronik_token_id_group.py                        | ○ Skipped | 0 s
chronik_token_parse_failure.py                   | ○ Skipped | 0 s
chronik_token_script_group.py                    | ○ Skipped | 0 s
chronik_token_slp_fungible.py                    | ○ Skipped | 0 s
chronik_token_slp_mint_vault.py                  | ○ Skipped | 0 s
chronik_token_slp_nft1.py                        | ○ Skipped | 0 s
chronik_tx.py                                    | ○ Skipped | 0 s
chronik_tx_removal_order.py                      | ○ Skipped | 0 s
chronik_unconfirmed_txs.py                       | ○ Skipped | 0 s
chronik_ws.py                                    | ○ Skipped | 0 s
chronik_ws_avalanche.py                          | ○ Skipped | 0 s
chronik_ws_ordering.py                           | ○ Skipped | 0 s
chronik_ws_ping.py                               | ○ Skipped | 0 s
chronik_ws_script.py                             | ○ Skipped | 0 s
feature_bind_port_discover.py                    | ○ Skipped | 0 s
feature_bind_port_externalip.py                  | ○ Skipped | 0 s
interface_usdt_net.py                            | ○ Skipped | 0 s
interface_usdt_utxocache.py                      | ○ Skipped | 0 s
interface_usdt_validation.py                     | ○ Skipped | 0 s

ALL                                              | ✓ Passed  | 1261 s (accumulated) 
Runtime: 107 s

ninja: build stopped: cannot make progress due to previous errors.
Build build-diff failed with exit code 1

@bot guix-linux guix-win guix-osx

PiRK published this revision for review.Apr 1 2026, 11:09

restore a line that allows the Options dialog in the GUI to enable NatPMP, and align the comment to the code's identation

This revision is now accepted and ready to land.Apr 1 2026, 15:23
PiRK edited the summary of this revision. (Show Details)
PiRK added a parent revision: D19778: Bump version to 0.33.1.

rebase for version bump

This revision was landed with ongoing or failed builds.Apr 3 2026, 07:53
This revision was automatically updated to reflect the committed changes.