Commit Graph

449 Commits

Author SHA1 Message Date
davide af19974381 fix: restore BIP44_COIN_TYPE=13496 for BitcoinPurple and fix LN stresstest race
BIP44_COIN_TYPE was accidentally set to 0 (Bitcoin mainnet), which would
cause BTCP wallets to derive keys identical to Bitcoin mainnet wallets from
the same seed. Restored to 13496 (provisional private constant matching the
BTCP P2P port) per tecnichal-data.md spec; updated table entry and test.

Also fixes a race condition in TestPeerDirectAnchors::test_payments_stresstest:
gath.cancel() was called immediately after OldTaskGroup exited, without any
await, so the event loop never ran the message-loop tasks to drain the final
revoke_and_ack for the last batch of 5 concurrent HTLCs. Added
asyncio.sleep(0) to yield one event-loop iteration before cancelling.
2026-05-05 19:26:26 +02:00
davide f4d2d0adea docs: add test suite report for BitcoinPurple Electrum (1005 passed, 6 skipped)
Full run: pytest tests -v, Python 3.12.3, pytest 9.0.3, ~3:30 min.

Documents pass/skip counts per file, reasons for the 6 upstream-skipped tests,

BTCP-specific coverage, and flaky test fixes applied in this session.
2026-05-05 14:10:48 +02:00
davide 5c406683b8 tests: use config.path instead of electrum_path for network-aware test dirs
SimpleConfig.path differs from electrum_path when the active network has a
subdirectory (e.g. BitcoinPurple mainnet). Tests that wrote directly to
electrum_path were resolving the wrong directory; use config.path consistently.
Also reorder setUp() so config is created before any path-dependent operations
2026-05-05 09:45:09 +02:00
davide 49ac312c88 tests: fix flaky LN peer tests (retries, timeouts, MPP wait loop)
- test_reestablish_fake_data: add 3-attempt retry per pay_invoice call to handle
  asyncio event-loop pressure on sequential payments
- test_htlc_switch_iteration_benchmark: raise timeout 2s → 5s
- test_payment_multipart_trampoline_e2e: attempts 1 → 3
- _run_trampoline_payment: default attempts 2 → 5; add outer retry loop catching
  NoPathFound (raised when all fee levels fail, bypassing the attempts counter)
- test_mpp_expiry (anchor): replace fixed asyncio.sleep with a polling loop that
  waits until bob has received both HTLCs before asserting MPP state
2026-05-05 09:45:09 +02:00
davide 7d433d0b44 tests: fix flaky LN tests (MPP timeout and orphaned tasks)
Two fixes:

1. test_trampoline_mpp_consolidation: set dave.MPP_EXPIRY=120 when
   test_mpp_consolidation=True. With MPP_EXPIRY=2s and sequential HTLC
   commitment rounds, Dave times out before the second HTLC arrives.

2. test_reestablish_fake_data: use explicit Task references in the
   payment setup phase so that loop tasks (message loops, htlc_switch)
   are cancelled in a finally block regardless of whether pay() succeeds
   or raises. Without this, asyncio.gather leaves orphaned tasks running
   across sub-test iterations when a payment fails, causing interference
2026-04-29 16:04:29 +02:00
davide 41e4a8141f tests: add BitcoinPurple test suite
46 tests across three classes:

TestBitcoinPurpleConstants — validates all BTCP network constants against
the technical specification: NET_NAME, TESTNET flags, CLI flags, address
prefixes (P2PKH 56, P2SH 55, WIF 0xb7), bech32/BOLT11 HRP, genesis hashes
and wire-order chain_hash, default ports, PoW parameters (adj_interval 120,
target_timespan 7200, MAX_TARGET, POW_GENESIS_BITS), SLIP-0132 HD key
headers, LN parameters, NETS_LIST uniqueness, and inheritance independence.

TestBitcoinPurpleDifficultyAdjustment — tests the 120-block retarget logic
using BitcoinPurple mainnet (testnet was wrong because get_target always
returns 0 on testnet).  Covers: genesis index returns genesis target from
POW_GENESIS_BITS, on-time/fast/slow adjustments, lower and upper clamp, the
120-block window (not 2016), and can_connect() calling get_target with the
correct period index.

TestBitcoinPurpleAddress — tests address encoding under BitcoinPurple: P2PKH
starts with 'P', bech32 with HRP 'btcp', address_to_script produces correct
P2WPKH scriptPubKey, WIF round-trip with prefix 0xb7, Bitcoin addresses
rejected on BTCP network
2026-04-29 10:08:33 +02:00
ThomasV 187ea80688 lazy_trampoline: adapt unit test 2026-04-28 10:22:10 +02:00
ThomasV f3a8dd61bb lazy trampoline:
If a trampoline forwarder fails to find a path, it may return a list
of trampolines it knows how to reach, so that clients can add these
trampolines to their route.

The list of trampolines and fees is written in the error data of
the 'update_fail_htlc' message.
2026-04-28 10:22:10 +02:00
ThomasV 5a31bf6aa0 Merge pull request #10463 from f321x/jit_2
lnwallet: zeroconf/just-in-time improvements and tests
2026-04-28 10:16:34 +02:00
f321x 306cac192b lnaddr: rename LnAddr -> bolt11
The LnAddr, lndecode and lnencode naming didn't imply that it is
bolt 11 specific, making it confusing to work with, now that there are
also bolt 12 "lnaddr".
Renaming it to *bolt11* creates a clear separation to bolt 12 things and
reduces mental load.

This commit is pure renaming (using the PyCharm IDE refactor function),
except for the removal of the `object` inheritance of LnAddr/BOLT11Addr,
this is Python 2 legacy.
2026-04-27 16:28:19 +02:00
ghost43 cc1874c9c9 Merge pull request #10575 from f321x/lnurlw_prefix
pi: handle lud-17 lnurl URIs
2026-04-24 15:00:07 +00:00
ghost43 8be4f8c8cf Merge pull request #10606 from f321x/trampoline_route_fees
trampoline: handle edges with known fees during route edge fee allocation
2026-04-24 14:46:40 +00:00
f321x 06fd088992 test_lnrouter: add unittests for tramp fee allocation
Adds `TestAllocateFeeBudget` for trampoline route fee allocation
coverage.

Co-Authored-By: SomberNight <somber.night@protonmail.com>
2026-04-24 16:34:52 +02:00
ghost43 b9dc6aa34c Merge pull request #10591 from SomberNight/202604_fix_wallet_mktx_base_tx
wallet: make_unsigned_tx: fix base_tx for GUI simple-send batching
2026-04-24 13:01:24 +00:00
ghost43 e96b833fe7 Merge pull request #10592 from SomberNight/202604_testnet_mainnet_mixup2
wallet_db: put 'genesis_blockhash' in DB, detect mainnet/testnet mixup. (db upgrade)
2026-04-24 12:59:31 +00:00
ThomasV 4b412de93c Merge pull request #10599 from f321x/fix_fw_fail_htlc
regtest: make fw_fail_htlc less flaky
2026-04-23 09:07:38 +02:00
ghost43 230e627559 Merge pull request #10600 from SomberNight/202604_lnpeer_chan_reest
lnpeer: channel_reestablish: split "they_are_ahead" into ctn vs revnum
2026-04-22 16:27:10 +00:00
f321x 14f202941e regtest: increase timeouts 30s -> 120s
I suspect the timeouts are a bit too short for the slow ci machine.
2026-04-22 18:00:52 +02:00
f321x 36e9f185d2 regtest: make fw_fail_htlc less flaky
On master fw_fail_htlc is, especially on the CI, flaky.
We mine 100 blocks, then wait fixed 5 seconds, then check if bob has
failed back the htlcs to alice.
However if the test runs slowly (CI) 5 seconds can be too short
for bob to catch up to the new 100 mined blocks.
Instead we should just use the wait_until_htlcs_settled helper function
which polls Alice local_unsettled_sent with 30 sec timeout, allowing bob
to take a bit longer (or be faster) than 5 s.

```
.***** test_fw_fail_htlc ******
initializing alice
funding alice
a101c8c4c22043ff42029bcab2f0bf6ce5482a60d656294cbec3a4df557e2687
initializing bob
funding bob
d323d572c54817116d185c91f15e449550c651eb4ed76891d3011e0a8eb4ef9a
initializing carol
funding carol
bbf3503663876a4ae00f70c7e58ad49318e83d5cf99d6effe692e113d10910c2
mining 1 blocks
starting daemon (PID 5559)
/tmp/alice/regtest/wallets/default_wallet
true
starting daemon (PID 5577)
/tmp/bob/regtest/wallets/default_wallet
true
starting daemon (PID 5595)
/tmp/carol/regtest/wallets/default_wallet
true
alice and carol open channels with bob
mining 3 blocks
wait until alice sees channel open.
wait until alice sees channel open..
wait until alice sees channel open...
alice pays carol
Daemon stopped
mining 1 blocks
mining 150 blocks
wait until 99ad1d44b9054f5a85c2fb45e9a9b93eb13c785104ed0664be5cf866d79d38fc:2 is spent.
...
wait until 99ad1d44b9054f5a85c2fb45e9a9b93eb13c785104ed0664be5cf866d79d38fc:2 is spent............................
mining 1 blocks
mining 100 blocks
alice htlc was not failed
FDaemon stopped
```
2026-04-22 17:12:30 +02:00
ThomasV 294d21407a Merge pull request #10568 from SomberNight/202604_verifier_left_sibling_duplicates
verifier.py: fix CVE-2012-2459: reject left-sibling duplicates
2026-04-21 16:22:07 +02:00
ThomasV c189360120 Merge pull request #10590 from SomberNight/202604_test_lnpeer_flaky_hold_invoice_set
tests: lnpeer: fix flaky test "hold_invoice_set_doesnt_get_expired"
2026-04-21 13:30:21 +02:00
SomberNight 45458c2f89 wallet_db: put 'genesis_blockhash' in DB, detect mainnet/testnet mixup
If the user tries to open a wallet for a different chain (mainnet vs testnet), try to show a reasonable error message.

See previous attempt at this: https://github.com/spesmilo/electrum/commit/c13e05770150c5210783c3d42d3d2b1a683f18b4, which added `wallet.test_addresses_sanity()`. However there are many codepaths where "random" exceptions might get raised before the Wallet object is even instantiated.
See [discussion there](https://github.com/spesmilo/electrum/commit/c13e05770150c5210783c3d42d3d2b1a683f18b4#commitcomment-28017341):
> should we actually fix that?
> if yes, it would be better to write the network type in storage

Indeed now I think we should do that. At the time I was concerned it would not help against altcoin forks if we put "mainnet" or "testnet" in the DB. Now I realise we should just put the genesis block hash in the DB instead.

Many of the reports in https://github.com/spesmilo/electrum/issues/6526 are likely due to users trying to open a mainnet wallet in testnet mode or vice-versa.

fixes https://github.com/spesmilo/electrum/issues/9134
    same issue in wizard 2fa two-step wallet-creation flow
2026-04-20 01:39:35 +00:00
SomberNight febe95e642 wallet: make_unsigned_tx: fix base_tx for GUI simple-send batching
fixes regression from https://github.com/spesmilo/electrum/commit/a9f20e4d3de9848247a9079b5b164d8006cf80db :
For the GUI / manual new tx constructions usecase, the flow relies on `base_tx.add_info_from_wallet(self)` being called before `base_tx_fee = base_tx.get_fee()`.

fixes https://github.com/spesmilo/electrum/issues/10587
maybe fixes https://github.com/spesmilo/electrum/issues/8876
    probably not a full fix: base_tx could have inputs for which add_info_from_wallet is not sufficient
2026-04-19 22:22:39 +00:00
SomberNight ca8bdba0c5 tests: lnpeer: fix flaky test "hold_invoice_set_doesnt_get_expired"
This test was flaky: the mpp_set resolution gets set to SETTLING several asyncio event loop iterations before the hold invoice callback "cb" gets called.
If the 0.1 sec polling triggers just in the middle of that interval, `assert cb_got_called` fails.

```
    async def check_mpp_state():
        async def wait_for_resolution():
            while True:
                await asyncio.sleep(0.1)
                if payment_key not in bob_w.received_mpp_htlcs:
                    continue
                if not bob_w.received_mpp_htlcs[payment_key].resolution == RecvMPPResolution.SETTLING:
                    continue
                return
        await util.wait_for2(wait_for_resolution(), timeout=2)
>       assert cb_got_called
E       assert False

tests/test_lnpeer.py:1898: AssertionError
```

see https://github.com/spesmilo/electrum/blob/16c8cb50e38c274cce8f9f66f28d8dd453f9f074/electrum/lnpeer.py#L3136-L3137

fixes https://github.com/spesmilo/electrum/issues/10589

-----

diff to reproduce the failure without present patch:
```
diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py
index 8669931c24..e15973d68f 100644
--- a/tests/test_lnpeer.py
+++ b/tests/test_lnpeer.py
@@ -1885,6 +1885,7 @@ class TestPeerDirect(TestPeer):
             cb_got_called = False
             async def cb(_payment_hash):
                 self.logger.debug(f"hold invoice callback called. {bob_w.network.get_local_height()=}")
+                await asyncio.sleep(1)
                 nonlocal cb_got_called
                 cb_got_called = True
```
2026-04-19 15:23:33 +00:00
SomberNight 21946e1e87 lnpeer: channel_reestablish: split "they_are_ahead" into ctn vs revnum 2026-04-18 17:23:35 +00:00
f321x ef702d74cd pi: handle lud-17 URI payment identifier
LNURL-W/P can also be encoded in lud-17 form instead of bech32.
https://github.com/lnurl/luds/blob/luds/17.md
e.g.
lnurlw://example.com/api/test123
lnurlp://example.com/api/test123
2026-04-13 09:30:28 +02:00
SomberNight 3d39074294 verifier.py: fix CVE-2012-2459: reject left-sibling duplicates
Due to how the txid-commitment merkle tree used in the block headers is constructed, we need an extra check to be able to validate the *position* of a txid in a block.

I think this is low severity for us.

See https://bitcointalk.org/?topic=102395 :

> The Merkle hash implementation that Bitcoin uses to calculate the Merkle
> root in a block header is flawed in that one can easily construct multiple
> lists of hashes that map to the same Merkle root.
> For example, merkle_hash([a, b, c]) and merkle_hash([a, b, c, c]) yield
> the same result. This is because, at every iteration, the Merkle hash
> function pads its intermediate list of hashes with the last hash if the
> list is of odd length, in order to make it of even length.
>
> And so, the Merkle root function can be effectively preimaged by
> changing the input so that one of the intermediate lists is of even
> length with the last two elements equal (where originally it was
> of odd length with a last element equal to the earlier mentioned two).
> As was later noted, this extends to any input length that is
> not a power of two:
> merkle_hash([a, b, c, d, e, f]) == merkle_hash([a, b, c, d, e, f, e, f]).
> Note that to maintain the same root hash, the only flexibility that
> exists is duplication of elements.

Ported from https://github.com/Electron-Cash/Electron-Cash/commit/165146362b4cb0ad74770b36aca1f9acb2800195

Co-authored-by: bitcoincashautist <80100588+A60AB5450353F40E@users.noreply.github.com>
2026-04-03 02:22:01 +00:00
f321x 4254c9a051 onion_message: fix route construction to ip
Don't include first hop of the path,
this is the hop from us to the first node and we don't
need a payload for ourselves.

Also adds unittest checking this.
2026-04-01 09:07:36 +02:00
Sander van Grieken 2e0f263269 onion_message: iterate blinded paths for onion message requests 2026-04-01 09:07:36 +02:00
Sander van Grieken 9bcbbdd3eb move blinding_privkey from onion_message to lnonion 2026-04-01 09:07:36 +02:00
f321x 2b6ad68145 tests: test_onion_message: mock LNWallet._add_peer
Mock LNWallet._add_peer so direct connection fallbacks don't
cause an exception.
2026-04-01 09:07:36 +02:00
Sander van Grieken 5c4fc2d713 onion_message: verify LNPeerAddr returned as hint in NoRouteFound 2026-04-01 09:07:36 +02:00
f321x 8d4affa293 test_onion_message: test get_blinded_paths_to_me
Add unittest to test the payment path of get_blinded_paths_to_me
2026-04-01 09:07:36 +02:00
Sander van Grieken 65fb739584 segwit_addr: bech32 decode without checksum option 2026-04-01 09:07:36 +02:00
f321x a06c8bacc3 lnpeer: don't signal OPTION_ZEROCONF_OPT to untrusted peer
Only signal `OPTION_ZEROCONF_OPT` to peers if we either:
1. Have no trusted peer configured (assuming that we are LSP)
2. Have a trusted peer configured, and the peer we are connecting
   to is this trusted peer.

Otherwise peers that are LSPs but are not the clients trusted LSP
might try to open a channel to the client but it would get rejected.
2026-03-26 17:39:05 +01:00
f321x 85356e5544 lnwallet: make jit fees configurable, add mining fees
Make the just in time channel fees and channel size
configvars, as in practice not every provider would
use the same hardcoded fees or channel sizes.
Add the mining fees required for the funding transaction on top of the
opening fees to prevent opening channels at a loss in a higher
fee environment.
2026-03-26 17:39:03 +01:00
f321x a3f12506ce tests: add unittests for LNWallet just in time opening
Adds unittests for `LNWallet.open_channel_just_in_time()`,
`LNWallet._cleanup_failed_jit_channel()`,
`LNWallet.can_get_zeroconf_channel()` and
`LNWallet.receive_requires_jit_channel()`.
2026-03-26 17:39:01 +01:00
f321x f56e1cafac lnworker: stop setting static jit alias for jit channel
...so we can have multiple just in time channels with the same lsp.
We already save a remote scid alias in `on_channel_ready` which we
already have received after the new zeroconf channel is in open state.
So setting the alias to the static node id hash is counterproductive
because it doesn't allow to differentiate between channels.

Also extends the regtest (`just_in_time`) to do a second channel
opening, to cover this scenario. This doesn't add much runtime to
the test, so the cost seems reasonable.
2026-03-26 17:38:58 +01:00
f321x 2da9fbbf15 lnworker/config: check if zeroconf is enabled when forwarding
On LSP side we were only checking if ACCEPT_ZEROCONF_CHANNELS
is enabled while forwarding a non-trampoline htlc.
During trampoline forwarding the config was ignored.

The ACCEPT_* prefix implied this was only for accepting inbound
zeroconf channels, but it also controls whether we open them when
forwarding HTLCs.

Renames the config var to OPEN_ZEROCONF_CHANNELS
to clarify it enables zeroconf channel opens in both directions,
and add the missing check when forwarding trampoline HTLCs.
2026-03-26 17:38:56 +01:00
f321x 1f17574dfa lnchannel: fix update_unfunded_state, add unittest
Fixes AbstractChannel.update_unfunded_state to stop calling a
non-existent method (unwatch_channel).
Adds unittest to execute the zeroconf path of update_unfunded_state.
2026-03-26 17:38:54 +01:00
f321x ac87eea02f test_lnwallet: unittest trampoline invoice_feature and r_tag
Add unittest that verifies we only include r_tags for trampoline nodes
if we signal trampoline support in the invoice_features and only signal
trampoline support if we use trampoline or have only open trampoline
channels.
2026-03-26 12:10:24 +01:00
ThomasV 31c2ffbf64 Merge pull request #10532 from spesmilo/variable_trampoline_onions
trampoline: allow trampoline onion packets of arbitrary size
2026-03-24 12:11:56 +01:00
SomberNight 42ad18b216 rm bip70 support
- could not find a single project that still actually cares about bip70 [0]
    - well except maybe BitPay.
        - but I cannot test with BitPay:
            - they have a testnet3 staging environment on test.bitpay.com
                - but the SSL cert they use for bip70 has expired in 2021
                - the webUI probably also has not been updated since then...
                    - they claim to have added LN support in 2022 in a blog post,
                        but it's not there on test.bitpay.com
            - on mainnet, they require KYC before payment
                - < ... angry noises >
            - their loss then, I don't care.
- this is code that no one wants to maintain

- this does not yet delete the signed bip70 payment data for historical txs
    - but it is no longer possible to export it from the GUI

[0]: https://bitcoinops.org/en/topics/bip70-payment-protocol/
2026-03-20 18:12:55 +00:00
ThomasV 7ee2477b68 Merge pull request #10528 from f321x/qt_swapserver_list_resize
swaps: improve SwapServerDialog, provider CLI, bugfixes
2026-03-19 09:42:05 +01:00
f321x f56e6318e3 swaps: make SwapManager.percentage Decimal
If SwapManager.percentage was a 0.2 float, rounding differences would
cause an exception in the fee calculation inverse sanity check when entering 20
000 sats into the SwapDialog. By making self.percentage a decimal we can
prevent this kind of issue.

```
File "/home/user/code/vibecoding_vm/electrum/electrum/gui/qt/swap_dialog.py", line 294, in on_send_edited
    recv_amount = self.swap_manager.get_recv_amount(send_amount, is_reverse=self.is_reverse)
  File "/home/user/code/vibecoding_vm/electrum/electrum/submarine_swaps.py", line 1320, in get_recv_amount
    if abs(send_amount - inverted_send_amount) > 1:
           ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for -: 'int' and 'NoneType'
```
2026-03-19 09:17:34 +01:00
ThomasV 558f85296c trampoline: allow trampoline onion packets of arbitrary size 2026-03-19 08:47:07 +01:00
SomberNight 44e99bc1a9 tests: move qml stuff to tests/qml/ 2026-03-16 13:11:48 +00:00
SomberNight b397ddb008 tests: move revealer and timelock_recovery stuff to tests/plugins/ 2026-03-16 00:52:48 +00:00
SomberNight c2f3729424 plugins: timelock_recovery: move checksum func to base class
don't import qt from tests unless it is qt being tested
2026-03-16 00:41:33 +00:00
SomberNight 907ceb9f52 tests: timelock_recovery plugin: add test vector for checksum from bip 2026-03-16 00:35:38 +00:00