Skip to content

Commit 83a6ced

Browse files
committed
fix: use 330 sat dust limit for P2TR/P2WPKH change outputs
Fixes #8395 Changelog-Fixed: Transactions now correctly create change outputs between 330-546 sat for P2TR/P2WPKH instead of absorbing them as fees
1 parent 85a930f commit 83a6ced

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

bitcoin/tx.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,12 +983,18 @@ struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw,
983983
size_t total_weight)
984984
{
985985
struct amount_sat fee = change_fee(feerate_perkw, total_weight);
986+
struct amount_sat dust_limit;
986987

987988
if (!amount_sat_sub(&excess, excess, fee))
988989
return AMOUNT_SAT(0);
989990

991+
if (chainparams->is_elements)
992+
dust_limit = AMOUNT_SAT(330); /* P2WPKH */
993+
else
994+
dust_limit = AMOUNT_SAT(330); /* P2TR */
995+
990996
/* Must be non-dust */
991-
if (!amount_sat_greater_eq(excess, chainparams->dust_limit))
997+
if (!amount_sat_greater_eq(excess, dust_limit))
992998
return AMOUNT_SAT(0);
993999

9941000
return excess;

tests/test_p2tr_change_dust.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
"""Test P2TR change outputs with dust limit 330 sat (issue #8395)."""
3+
import unittest
4+
from pyln.testing.fixtures import *
5+
from pyln.testing.utils import only_one, TEST_NETWORK, wait_for
6+
7+
8+
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "P2TR not yet supported on Elements")
9+
def test_p2tr_change_dust_limit(node_factory, bitcoind):
10+
11+
l1 = node_factory.get_node(feerates=(253, 253, 253, 253))
12+
13+
addr = l1.rpc.newaddr('p2tr')['p2tr']
14+
bitcoind.rpc.sendtoaddress(addr, 1.0)
15+
bitcoind.generate_block(1)
16+
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
17+
18+
outputs = l1.rpc.listfunds()['outputs']
19+
assert len(outputs) == 1
20+
utxo = outputs[0]
21+
22+
utxo_amount = int(utxo['amount_msat'] / 1000)
23+
24+
target_amount = utxo_amount - 450
25+
26+
result = l1.rpc.fundpsbt(
27+
satoshi=f"{target_amount}sat",
28+
feerate="253perkw",
29+
startweight=0,
30+
excess_as_change=True
31+
)
32+
33+
assert 'change_outnum' in result, "Expected change output to be created"
34+
35+
psbt = bitcoind.rpc.decodepsbt(result['psbt'])
36+
37+
change_outnum = result['change_outnum']
38+
if 'tx' in psbt:
39+
change_output = psbt['tx']['vout'][change_outnum]
40+
change_amount_btc = float(change_output['value'])
41+
else:
42+
change_output = psbt['outputs'][change_outnum]
43+
change_amount_btc = float(change_output['amount'])
44+
45+
change_amount_sat = int(change_amount_btc * 100_000_000)
46+
47+
print(f"Change amount: {change_amount_sat} sat")
48+
49+
assert change_amount_sat >= 330, f"Change {change_amount_sat} sat should be >= 330 sat"
50+
assert change_amount_sat <= 546, f"Change {change_amount_sat} sat should be <= 546 sat (for this test)"
51+
52+
print(f"SUCCESS: P2TR change output of {change_amount_sat} sat created (between 330 and 546 sat)")

0 commit comments

Comments
 (0)