Skip to content

Commit 853e299

Browse files
committed
splice: Add test for splice-out and fix errors
Added a test for splicing out that exposed some behavior and code glitches that are addressed in this commit. Also added documentation for how to do a splice out . ChangeLog-Fixed: Added docs, testing, and some fixes related to splicing out.
1 parent dbec2b6 commit 853e299

File tree

5 files changed

+125
-60
lines changed

5 files changed

+125
-60
lines changed

channeld/channeld.c

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
((msg) == WIRE_SPLICE || \
6262
(msg) == WIRE_SPLICE_ACK)
6363

64+
#define SAT_MIN(a, b) (amount_sat_less((a), (b)) ? (a) : (b))
65+
6466
struct peer {
6567
struct per_peer_state *pps;
6668
bool channel_ready[NUM_SIDES];
@@ -2907,7 +2909,7 @@ static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt)
29072909
weight += psbt_input_get_weight(psbt, i);
29082910

29092911
for (size_t i = 0; i < psbt->num_outputs; i++)
2910-
if (is_initiators_serial(&psbt->inputs[i].unknowns)) {
2912+
if (is_initiators_serial(&psbt->outputs[i].unknowns)) {
29112913
if (role == TX_INITIATOR)
29122914
weight += psbt_output_get_weight(psbt, i);
29132915
}
@@ -2928,7 +2930,7 @@ static struct amount_sat check_balances(struct peer *peer,
29282930
{
29292931
struct amount_sat min_initiator_fee, min_accepter_fee,
29302932
max_initiator_fee, max_accepter_fee,
2931-
funding_amount_res;
2933+
funding_amount_res, min_multiplied;
29322934
struct amount_msat funding_amount,
29332935
initiator_fee, accepter_fee;
29342936
struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES];
@@ -2977,45 +2979,23 @@ static struct amount_sat check_balances(struct peer *peer,
29772979
* While we're, here, adjust the output counts by splice amount.
29782980
*/
29792981

2980-
if(peer->splicing->opener_relative > 0) {
2981-
if (!amount_msat_add_sat(&funding_amount, funding_amount,
2982-
amount_sat((u64)peer->splicing->opener_relative)))
2983-
peer_failed_warn(peer->pps, &peer->channel_id,
2984-
"Unable to add opener funding");
2985-
if (!amount_msat_add_sat(&out[TX_INITIATOR], out[TX_INITIATOR],
2986-
amount_sat((u64)peer->splicing->opener_relative)))
2987-
peer_failed_warn(peer->pps, &peer->channel_id,
2988-
"Unable to add opener funding to out amnt.");
2989-
} else {
2990-
if (!amount_msat_sub_sat(&funding_amount, funding_amount,
2991-
amount_sat((u64)-peer->splicing->opener_relative)))
2992-
peer_failed_warn(peer->pps, &peer->channel_id,
2993-
"Unable to sub opener funding");
2994-
if (!amount_msat_sub_sat(&out[TX_INITIATOR], out[TX_INITIATOR],
2995-
amount_sat((u64)peer->splicing->opener_relative)))
2996-
peer_failed_warn(peer->pps, &peer->channel_id,
2997-
"Unable to sub opener funding from out amnt.");
2998-
}
2982+
if (!amount_msat_add_sat_s64(&funding_amount, funding_amount,
2983+
peer->splicing->opener_relative))
2984+
peer_failed_warn(peer->pps, &peer->channel_id,
2985+
"Unable to add opener funding");
2986+
if (!amount_msat_add_sat_s64(&out[TX_INITIATOR], out[TX_INITIATOR],
2987+
peer->splicing->opener_relative))
2988+
peer_failed_warn(peer->pps, &peer->channel_id,
2989+
"Unable to add opener funding to out amnt.");
29992990

3000-
if(peer->splicing->accepter_relative > 0) {
3001-
if (!amount_msat_add_sat(&funding_amount, funding_amount,
3002-
amount_sat((u64)peer->splicing->accepter_relative)))
3003-
peer_failed_warn(peer->pps, &peer->channel_id,
3004-
"Unable to add accepter funding");
3005-
if (!amount_msat_add_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER],
3006-
amount_sat((u64)peer->splicing->accepter_relative)))
3007-
peer_failed_warn(peer->pps, &peer->channel_id,
3008-
"Unable to add accepter funding to out amnt.");
3009-
} else {
3010-
if (!amount_msat_sub_sat(&funding_amount, funding_amount,
3011-
amount_sat((u64)-peer->splicing->accepter_relative)))
3012-
peer_failed_warn(peer->pps, &peer->channel_id,
3013-
"Unable to subtract accepter funding");
3014-
if (!amount_msat_sub_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER],
3015-
amount_sat((u64)-peer->splicing->accepter_relative)))
3016-
peer_failed_warn(peer->pps, &peer->channel_id,
3017-
"Unable to sub accepter funding from out amnt.");
3018-
}
2991+
if (!amount_msat_add_sat_s64(&funding_amount, funding_amount,
2992+
peer->splicing->accepter_relative))
2993+
peer_failed_warn(peer->pps, &peer->channel_id,
2994+
"Unable to add accepter funding");
2995+
if (!amount_msat_add_sat_s64(&out[TX_ACCEPTER], out[TX_ACCEPTER],
2996+
peer->splicing->accepter_relative))
2997+
peer_failed_warn(peer->pps, &peer->channel_id,
2998+
"Unable to add accepter funding to out amnt.");
30192999

30203000
if (amount_msat_less(in[TX_INITIATOR], out[TX_INITIATOR])) {
30213001
msg = towire_channeld_splice_funding_error(NULL, in[TX_INITIATOR],
@@ -3064,6 +3044,14 @@ static struct amount_sat check_balances(struct peer *peer,
30643044
max_initiator_fee = amount_tx_fee(peer->feerate_max,
30653045
calc_weight(TX_INITIATOR, psbt));
30663046

3047+
/* Sometimes feerate_max is some absurdly high value, in that case we
3048+
* give a fee warning based of a multiple of the min value. */
3049+
amount_sat_mul(&min_multiplied, min_accepter_fee, 5);
3050+
max_accepter_fee = SAT_MIN(min_multiplied, max_accepter_fee);
3051+
3052+
amount_sat_mul(&min_multiplied, min_initiator_fee, 5);
3053+
max_initiator_fee = SAT_MIN(min_multiplied, max_initiator_fee);
3054+
30673055
/* Check initiator fee */
30683056
if (amount_msat_less_sat(initiator_fee, min_initiator_fee)) {
30693057
msg = towire_channeld_splice_feerate_error(NULL, initiator_fee,

channeld/inflight.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@ struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *
1212
inflight->amnt = fromwire_amount_sat(cursor, max);
1313
inflight->psbt = fromwire_wally_psbt(inflight, cursor, max);
1414
inflight->splice_amnt = fromwire_s64(cursor, max);
15-
inflight->last_tx = fromwire_bitcoin_tx(inflight, cursor, max);
16-
fromwire_bitcoin_signature(cursor, max, &inflight->last_sig);
15+
int has_tx = fromwire_u8(cursor, max);
16+
if(has_tx) {
17+
inflight->last_tx = fromwire_bitcoin_tx(inflight, cursor, max);
18+
fromwire_bitcoin_signature(cursor, max, &inflight->last_sig);
19+
}
20+
else {
21+
inflight->last_tx = NULL;
22+
memset(&inflight->last_sig, 0, sizeof(inflight->last_sig));
23+
}
1724
inflight->i_am_initiator = fromwire_bool(cursor, max);
1825

1926
return inflight;
@@ -25,8 +32,11 @@ void towire_inflight(u8 **pptr, const struct inflight *inflight)
2532
towire_amount_sat(pptr, inflight->amnt);
2633
towire_wally_psbt(pptr, inflight->psbt);
2734
towire_s64(pptr, inflight->splice_amnt);
28-
towire_bitcoin_tx(pptr, inflight->last_tx);
29-
towire_bitcoin_signature(pptr, &inflight->last_sig);
35+
towire_u8(pptr, inflight->last_tx ? 1 : 0);
36+
if(inflight->last_tx) {
37+
towire_bitcoin_tx(pptr, inflight->last_tx);
38+
towire_bitcoin_signature(pptr, &inflight->last_sig);
39+
}
3040
towire_bool(pptr, inflight->i_am_initiator);
3141
}
3242

doc/lightning-splice_init.7.md

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,58 @@ our\_bytes\_in\_splice\_tx / 1000.
2929
provided looks too high. This is to protect against accidentally setting your
3030
fee higher than intended. Set `force_feerate` to true to skip this saftey check.
3131

32+
Note you may need to add a double dash (\-\-) after splice\_init if using a negative
33+
*relative\_amount* so it is not interpretted as a command modifier. For example:
34+
```shell
35+
lightning-cli splice_init -- $CHANNEL_ID -100000
36+
```
37+
3238
Here is an example set of splice commands that will splice in 100,000 sats to
3339
the first channel that comes out of `listpeerchannels`. The example assumes
3440
you already have at least one confirmed channel.
3541
```shell
36-
RESULT=$(lightning-cli listpeerchannels)
37-
CHANNEL_ID=$(echo $RESULT| jq -r ".channels[0].channel_id")
38-
echo $RESULT
42+
RESULT=$(lightning-cli listpeerchannels);
43+
CHANNEL_ID=$(echo $RESULT| jq -r ".channels[0].channel_id");
44+
echo $RESULT;
45+
46+
RESULT=$(lightning-cli fundpsbt -k satoshi=100000sat feerate=urgent startweight=800 excess_as_change=true);
47+
INITIALPSBT=$(echo $RESULT | jq -r ".psbt");
48+
echo $RESULT;
49+
50+
RESULT=$(lightning-cli splice_init $CHANNEL_ID 100000 $INITIALPSBT);
51+
PSBT=$(echo $RESULT | jq -r ".psbt");
52+
echo $RESULT;
53+
54+
RESULT=$(lightning-cli splice_update $CHANNEL_ID $PSBT);
55+
PSBT=$(echo $RESULT | jq -r ".psbt");
56+
echo $RESULT;
3957

40-
RESULT=$(lightning-cli fundpsbt -k satoshi=100000sat feerate=urgent startweight=800 excess_as_change=true)
41-
INITIALPSBT=$(echo $RESULT | jq -r ".psbt")
42-
echo $RESULT
58+
RESULT=$(lightning-cli signpsbt -k psbt="$PSBT");
59+
PSBT=$(echo $RESULT | jq -r ".signed_psbt");
60+
echo $RESULT;
61+
62+
lightning-cli splice_signed $CHANNEL_ID $PSBT
63+
```
64+
65+
Here is an example set of splice commands that will splice out 100,000 sats from
66+
first channel that comes out of `listpeerchannels`. The example assumes
67+
you already have at least one confirmed channel.
68+
```shell
69+
RESULT=$(lightning-cli listpeerchannels);
70+
CHANNEL_ID=$(echo $RESULT| jq -r ".channels[0].channel_id");
71+
echo $RESULT;
4372

44-
RESULT=$(lightning-cli splice_init $CHANNEL_ID 100000 $INITIALPSBT)
45-
PSBT=$(echo $RESULT | jq -r ".psbt")
46-
echo $RESULT
73+
RESULT=$(lightning-cli recvpsbt 100000);
74+
INITIALPSBT=$(echo $RESULT | jq -r ".psbt");
75+
echo $RESULT;
4776

48-
RESULT=$(lightning-cli splice_update $CHANNEL_ID $PSBT)
49-
PSBT=$(echo $RESULT | jq -r ".psbt")
50-
echo $RESULT
77+
RESULT=$(lightning-cli splice_init -- $CHANNEL_ID -100500 $INITIALPSBT);
78+
PSBT=$(echo $RESULT | jq -r ".psbt");
79+
echo $RESULT;
5180

52-
RESULT=$(lightning-cli signpsbt -k psbt="$PSBT")
53-
PSBT=$(echo $RESULT | jq -r ".signed_psbt")
54-
echo $RESULT
81+
RESULT=$(lightning-cli splice_update $CHANNEL_ID $PSBT);
82+
PSBT=$(echo $RESULT | jq -r ".psbt");
83+
echo $RESULT;
5584

5685
lightning-cli splice_signed $CHANNEL_ID $PSBT
5786
```

lightningd/channel_control.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,10 @@ bool peer_start_channeld(struct channel *channel,
14171417
infcopy->outpoint = inflight->funding->outpoint;
14181418
infcopy->amnt = inflight->funding->total_funds;
14191419
infcopy->splice_amnt = inflight->funding->splice_amnt;
1420-
infcopy->last_tx = tal_dup(infcopy, struct bitcoin_tx, inflight->last_tx);
1420+
if (inflight->last_tx)
1421+
infcopy->last_tx = tal_dup(infcopy, struct bitcoin_tx, inflight->last_tx);
1422+
else
1423+
infcopy->last_tx = NULL;
14211424
infcopy->last_sig = inflight->last_sig;
14221425
infcopy->i_am_initiator = inflight->i_am_initiator;
14231426
tal_wally_start();

tests/test_splicing.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,38 @@ def test_splice(node_factory, bitcoind):
3939
# Check that the splice doesn't generate a unilateral close transaction
4040
time.sleep(5)
4141
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
42+
43+
44+
@pytest.mark.openchannel('v1')
45+
@pytest.mark.openchannel('v2')
46+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
47+
def test_splice_out(node_factory, bitcoind):
48+
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
49+
50+
chan_id = l1.get_channel_id(l2)
51+
52+
funds_result = l1.rpc.recvpsbt(100000)
53+
54+
# Pay with fee by subjtracting 5000 from channel balance
55+
result = l1.rpc.splice_init(chan_id, -105000, funds_result['psbt'])
56+
result = l1.rpc.splice_update(chan_id, result['psbt'])
57+
result = l1.rpc.splice_signed(chan_id, result['psbt'])
58+
59+
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
60+
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
61+
62+
mempool = bitcoind.rpc.getrawmempool(True)
63+
assert len(list(mempool.keys())) == 1
64+
assert result['txid'] in list(mempool.keys())
65+
66+
bitcoind.generate_block(6, wait_for_mempool=1)
67+
68+
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
69+
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
70+
71+
inv = l2.rpc.invoice(10**2, '3', 'no_3')
72+
l1.rpc.pay(inv['bolt11'])
73+
74+
# Check that the splice doesn't generate a unilateral close transaction
75+
time.sleep(5)
76+
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0

0 commit comments

Comments
 (0)