channeld: Code to implement splicing

Update the lightningd <-> channeld interface with lots of new commands to needed to facilitate spicing.

Implement the channeld splicing protocol leveraging the interactivetx protocol.

Implement lightningd’s channel_control to support channeld in its splicing efforts.

Changelog-Added: Added the features to enable splicing & resizing of active channels.
This commit is contained in:
Dusty Daemon 2023-07-27 14:37:52 -07:00 committed by Rusty Russell
parent ebd0a3fd69
commit 4628e3ace8
88 changed files with 4560 additions and 644 deletions

View file

@ -263,6 +263,15 @@ jobs:
TEST_NETWORK: regtest
DEVELOPER: 1
EXPERIMENTAL_DUAL_FUND: 1
# And splicing!
- NAME: splicing
CFG: gcc-dev1
TEST_DB_PROVIDER: sqlite3
COMPILER: gcc
TEST_NETWORK: regtest
DEVELOPER: 1
EXPERIMENTAL_DUAL_FUND: 1
EXPERIMENTAL_SPLICING: 1
steps:
- name: Checkout
uses: actions/checkout@v3
@ -290,6 +299,7 @@ jobs:
DEVELOPER: ${{ matrix.DEVELOPER }}
COMPILER: ${{ matrix.COMPILER }}
EXPERIMENTAL_DUAL_FUND: ${{ matrix.EXPERIMENTAL_DUAL_FUND }}
EXPERIMENTAL_SPLICING: ${{ matrix.EXPERIMENTAL_SPLICING }}
COMPAT: 1
CFG: ${{ matrix.CFG }}
SLOW_MACHINE: 1

View file

@ -182,6 +182,7 @@
"ListpeerchannelsChannelsState": {
"AWAITING_UNILATERAL": 6,
"CHANNELD_AWAITING_LOCKIN": 1,
"CHANNELD_AWAITING_SPLICE": 11,
"CHANNELD_NORMAL": 2,
"CHANNELD_SHUTTING_DOWN": 3,
"CLOSINGD_COMPLETE": 5,
@ -1131,6 +1132,7 @@
"ListPeerChannels.channels[].inflight[].funding_txid": 1,
"ListPeerChannels.channels[].inflight[].our_funding_msat": 5,
"ListPeerChannels.channels[].inflight[].scratch_txid": 6,
"ListPeerChannels.channels[].inflight[].splice_amount": 7,
"ListPeerChannels.channels[].inflight[].total_funding_msat": 4
},
"ListpeerchannelsChannelsState_changes": {
@ -1241,6 +1243,7 @@
"ListPeers.peers[].channels[].inflight[].funding_txid": 1,
"ListPeers.peers[].channels[].inflight[].our_funding_msat": 5,
"ListPeers.peers[].channels[].inflight[].scratch_txid": 6,
"ListPeers.peers[].channels[].inflight[].splice_amount": 7,
"ListPeers.peers[].channels[].inflight[].total_funding_msat": 4
},
"ListpeersPeersLog": {
@ -4078,6 +4081,10 @@
"added": "v23.02",
"deprecated": false
},
"ListPeerChannels.channels[].inflight[].splice_amount": {
"added": "v23.08",
"deprecated": false
},
"ListPeerChannels.channels[].inflight[].total_funding_msat": {
"added": "v23.02",
"deprecated": false
@ -4422,6 +4429,10 @@
"added": "pre-v0.10.1",
"deprecated": false
},
"ListPeers.peers[].channels[].inflight[].splice_amount": {
"added": "v23.08",
"deprecated": false
},
"ListPeers.peers[].channels[].inflight[].total_funding_msat": {
"added": "pre-v0.10.1",
"deprecated": false

View file

@ -322,6 +322,15 @@ void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in,
assert(wally_err == WALLY_OK);
}
void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in,
struct bitcoin_outpoint outpoint)
{
assert(in < psbt->num_inputs);
psbt->inputs[in].index = outpoint.n;
memcpy(psbt->inputs[in].txhash, &outpoint.txid,
sizeof(struct bitcoin_txid));
}
void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript)
{
int wally_err;
@ -446,6 +455,28 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt,
return val;
}
size_t psbt_input_get_weight(const struct wally_psbt *psbt,
size_t in)
{
size_t weight;
const struct wally_map_item *redeem_script;
redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04);
/* txid + txout + sequence */
weight = (32 + 4 + 4) * 4;
if (redeem_script) {
weight +=
(redeem_script->value_len +
varint_size(redeem_script->value_len)) * 4;
} else {
/* zero scriptSig length */
weight += varint_size(0) * 4;
}
return weight;
}
struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt,
size_t out)
{
@ -456,6 +487,13 @@ struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt,
return amount_asset_to_sat(&asset);
}
size_t psbt_output_get_weight(const struct wally_psbt *psbt,
size_t outnum)
{
return (8 /* amount*/ + varint_size(psbt->outputs[outnum].script_len) +
psbt->outputs[outnum].script_len) * 4;
}
static void add(u8 **key, const void *mem, size_t len)
{
size_t oldlen = tal_count(*key);
@ -733,6 +771,12 @@ const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt,
return bytes;
}
bool validate_psbt(const struct wally_psbt *psbt)
{
size_t len;
return wally_psbt_get_length(psbt, 0, &len) == WALLY_OK;
}
struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes,
size_t byte_len)
{

View file

@ -127,6 +127,9 @@ void psbt_input_set_wit_utxo(struct wally_psbt *psbt, size_t in,
void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in,
const struct wally_tx *prev_tx);
void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in,
struct bitcoin_outpoint outpoint);
/* psbt_elements_input_set_asset - Set the asset/value fields for an
* Elements PSBT (PSET, technically */
void psbt_elements_input_set_asset(struct wally_psbt *psbt, size_t in,
@ -211,6 +214,10 @@ void psbt_output_set_unknown(const tal_t *ctx,
struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt,
size_t in);
/* psbt_input_get_weight - Calculate the tx weight for input index `in` */
size_t psbt_input_get_weight(const struct wally_psbt *psbt,
size_t in);
/* psbt_output_get_amount - Returns the value of this output
*
* @psbt - psbt
@ -219,6 +226,10 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt,
struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt,
size_t out);
/* psbt_output_get_weight - Calculate the tx weight for output index `outnum` */
size_t psbt_output_get_weight(const struct wally_psbt *psbt,
size_t outnum);
/* psbt_compute_fee - Returns value of fee for PSBT
*
* @psbt -psbt
@ -266,6 +277,7 @@ struct wally_psbt *psbt_from_b64(const tal_t *ctx,
char *psbt_to_b64(const tal_t *ctx, const struct wally_psbt *psbt);
const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt,
size_t *bytes_written);
bool validate_psbt(const struct wally_psbt *psbt);
struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes,
size_t byte_len);
void towire_wally_psbt(u8 **pptr, const struct wally_psbt *psbt);

View file

@ -13,6 +13,8 @@ CHANNELD_HEADERS := \
CHANNELD_SRC := channeld/channeld.c \
channeld/commit_tx.c \
channeld/full_channel.c \
channeld/splice.c \
channeld/inflight.c \
channeld/channeld_wiregen.c \
channeld/watchtower.c
@ -25,8 +27,13 @@ ALL_C_HEADERS += $(CHANNELD_HEADERS)
ALL_PROGRAMS += lightningd/lightning_channeld
# Here's what lightningd depends on
LIGHTNINGD_CONTROL_HEADERS += channeld/channeld_wiregen.h
LIGHTNINGD_CONTROL_OBJS += channeld/channeld_wiregen.o
LIGHTNINGD_CONTROL_HEADERS += \
channeld/channeld_wiregen.h \
channeld/inflight.h
LIGHTNINGD_CONTROL_OBJS += \
channeld/channeld_wiregen.o \
channeld/inflight.o
# Common source we use.
CHANNELD_COMMON_OBJS := \
@ -53,6 +60,7 @@ CHANNELD_COMMON_OBJS := \
common/status_wiregen.o \
common/gossip_store.o \
common/hmac.o \
common/interactivetx.o \
common/htlc_state.o \
common/htlc_trim.o \
common/htlc_tx.o \
@ -73,6 +81,7 @@ CHANNELD_COMMON_OBJS := \
common/ping.o \
common/psbt_keypath.o \
common/psbt_open.o \
common/psbt_internal.o \
common/private_channel_announcement.o \
common/pseudorand.o \
common/read_peer_msg.o \

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
#include <bitcoin/psbt.h>
#include <bitcoin/tx.h>
#include <channeld/inflight.h>
#include <common/bip32.h>
#include <common/blockheight_states.h>
#include <common/cryptomsg.h>
@ -83,6 +84,8 @@ msgdata,channeld_init,reestablish_only,bool,
msgdata,channeld_init,channel_update_len,u16,
msgdata,channeld_init,channel_update,u8,channel_update_len
msgdata,channeld_init,experimental_upgrade,bool,
msgdata,channeld_init,num_inflights,u16,
msgdata,channeld_init,inflights,inflight,num_inflights
# master->channeld funding hit new depth(funding locked if >= lock depth)
# alias != NULL if zeroconf and short_channel_id == NULL
@ -91,6 +94,8 @@ msgtype,channeld_funding_depth,1002
msgdata,channeld_funding_depth,short_channel_id,?short_channel_id,
msgdata,channeld_funding_depth,alias_local,?short_channel_id,
msgdata,channeld_funding_depth,depth,u32,
msgdata,channeld_funding_depth,splicing,bool,
msgdata,channeld_funding_depth,txid,bitcoin_txid,
# Tell channel to offer this htlc
msgtype,channeld_offer_htlc,1004
@ -122,6 +127,12 @@ msgtype,channeld_got_channel_ready,1019
msgdata,channeld_got_channel_ready,next_per_commit_point,pubkey,
msgdata,channeld_got_channel_ready,alias,?short_channel_id,
# When we receive funding_locked.
msgtype,channeld_got_splice_locked,1119
msgdata,channeld_got_splice_locked,funding_sats,amount_sat,
msgdata,channeld_got_splice_locked,splice_amnt,s64,
msgdata,channeld_got_splice_locked,locked_txid,bitcoin_txid,
#include <common/penalty_base.h>
# When we send a commitment_signed message, tell master.
@ -160,6 +171,14 @@ msgdata,channeld_got_commitsig,failed,failed_htlc,num_failed
msgdata,channeld_got_commitsig,num_changed,u16,
msgdata,channeld_got_commitsig,changed,changed_htlc,num_changed
msgdata,channeld_got_commitsig,tx,bitcoin_tx,
# Inflight splice commitments
msgdata,channeld_got_commitsig,num_inflight_commitsigs,u16,
msgdata,channeld_got_commitsig,inflight_commitsigs,commitsig,num_inflight_commitsigs
subtype,commitsig
subtypedata,commitsig,tx,bitcoin_tx,
subtypedata,commitsig,commit_signature,bitcoin_signature,
subtypedata,commitsig,num_htlcs,u16,
subtypedata,commitsig,htlc_signatures,bitcoin_signature,num_htlcs
# Wait for reply, to make sure it's on disk before we send revocation.
msgtype,channeld_got_commitsig_reply,1121
@ -182,6 +201,79 @@ msgdata,channeld_got_revoke,penalty_tx,?bitcoin_tx,
msgtype,channeld_got_revoke_reply,1122
#include <wally_bip32.h>
# master->channeld: hello, I'd like to start a channel splice open
msgtype,channeld_splice_init,7204
msgdata,channeld_splice_init,psbt,wally_psbt,
msgdata,channeld_splice_init,relative_amount,s64,
msgdata,channeld_splice_init,feerate_per_kw,u32,
msgdata,channeld_splice_init,force_feerate,bool,
# channeld->master: hello, I started a channel splice open
msgtype,channeld_splice_confirmed_init,7205
msgdata,channeld_splice_confirmed_init,psbt,wally_psbt,
# master->channeld: Update an active splice
msgtype,channeld_splice_update,7206
msgdata,channeld_splice_update,psbt,wally_psbt,
# channeld->master: Splice update complete
msgtype,channeld_splice_confirmed_update,7207
msgdata,channeld_splice_confirmed_update,psbt,wally_psbt,
msgdata,channeld_splice_confirmed_update,commitments_secured,bool,
# channeld->master: Lookup a transaction
msgtype,channeld_splice_lookup_tx,7208
msgdata,channeld_splice_lookup_tx,txid,bitcoin_txid,
# master->channeld: Retrieved transaction
msgtype,channeld_splice_lookup_tx_result,7209
msgdata,channeld_splice_lookup_tx_result,tx,bitcoin_tx,
# master->channeld: User has signed psbt and it's ready to complete
msgtype,channeld_splice_signed,7212
msgdata,channeld_splice_signed,psbt,wally_psbt,
msgdata,channeld_splice_signed,force_sign_first,bool,
# channeld->master: Signed psbt is completed
msgtype,channeld_splice_confirmed_signed,7213
msgdata,channeld_splice_confirmed_signed,tx,bitcoin_tx,
msgdata,channeld_splice_confirmed_signed,output_index,u32,
# channeld->master: A feerate error has occured
msgtype,channeld_splice_feerate_error,7215
msgdata,channeld_splice_feerate_error,fee,amount_msat,
msgdata,channeld_splice_feerate_error,too_high,bool,
# channeld->master: Add an inflight to the DB
msgtype,channeld_add_inflight,7216
msgdata,channeld_add_inflight,tx_id,bitcoin_txid,
msgdata,channeld_add_inflight,tx_outnum,u32,
msgdata,channeld_add_inflight,feerate,u32,
msgdata,channeld_add_inflight,satoshis,amount_sat,
msgdata,channeld_add_inflight,splice_amount,s64,
msgdata,channeld_add_inflight,psbt,wally_psbt,
msgdata,channeld_add_inflight,i_am_initiator,bool,
# master->channeld: Inflight saved successfully
msgtype,channeld_got_inflight,7217
# channeld->master: Update inflight with sigs
msgtype,channeld_update_inflight,7219
msgdata,channeld_update_inflight,psbt,wally_psbt,
msgdata,channeld_update_inflight,last_tx,?bitcoin_tx,
msgdata,channeld_update_inflight,last_sig,?bitcoin_signature,
# channeld->master: A funding error has occured
msgtype,channeld_splice_funding_error,7220
msgdata,channeld_splice_funding_error,funding,amount_msat,
msgdata,channeld_splice_funding_error,req_funding,amount_msat,
msgdata,channeld_splice_funding_error,opener_error,bool,
# channeld->master: A splice state error has occured
msgtype,channeld_splice_state_error,7221
msgdata,channeld_splice_state_error,state_error,wirestring,
# Tell peer to shut down channel.
msgtype,channeld_send_shutdown,1023
msgdata,channeld_send_shutdown,final_index,?u32,

Can't render this file because it has a wrong number of fields in line 15.

View file

@ -308,10 +308,30 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,
const struct pubkey *per_commitment_point,
u64 commitment_number,
enum side side)
{
return channel_splice_txs(ctx, &channel->funding, channel->funding_sats,
htlcmap, direct_outputs, funding_wscript,
channel, per_commitment_point,
commitment_number, side, 0, 0);
}
struct bitcoin_tx **channel_splice_txs(const tal_t *ctx,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
const struct htlc ***htlcmap,
struct wally_tx_output *direct_outputs[NUM_SIDES],
const u8 **funding_wscript,
const struct channel *channel,
const struct pubkey *per_commitment_point,
u64 commitment_number,
enum side side,
s64 splice_amnt,
s64 remote_splice_amnt)
{
struct bitcoin_tx **txs;
const struct htlc **committed;
struct keyset keyset;
struct amount_msat side_pay, other_side_pay;
if (!derive_keyset(per_commitment_point,
&channel->basepoints[side],
@ -329,10 +349,25 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,
&channel->funding_pubkey[side],
&channel->funding_pubkey[!side]);
side_pay = channel->view[side].owed[side];
other_side_pay = channel->view[side].owed[!side];
if (side == LOCAL) {
if (!amount_msat_add_sat_s64(&side_pay, side_pay, splice_amnt))
return NULL;
if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, remote_splice_amnt))
return NULL;
} else if (side == REMOTE) {
if (!amount_msat_add_sat_s64(&side_pay, side_pay, remote_splice_amnt))
return NULL;
if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, splice_amnt))
return NULL;
}
txs = tal_arr(ctx, struct bitcoin_tx *, 1);
txs[0] = commit_tx(
ctx, &channel->funding,
channel->funding_sats,
txs, funding,
funding_sats,
&channel->funding_pubkey[side],
&channel->funding_pubkey[!side],
channel->opener,
@ -340,8 +375,8 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,
channel->lease_expiry,
channel_blockheight(channel, side),
&keyset, channel_feerate(channel, side),
channel->config[side].dust_limit, channel->view[side].owed[side],
channel->view[side].owed[!side], committed, htlcmap, direct_outputs,
channel->config[side].dust_limit, side_pay,
other_side_pay, committed, htlcmap, direct_outputs,
commitment_number ^ channel->commitment_number_obscurer,
channel_has(channel, OPT_ANCHOR_OUTPUTS),
channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX),
@ -371,8 +406,22 @@ static bool get_room_above_reserve(const struct channel *channel,
/* Reserve is set by the *other* side */
struct amount_sat reserve = channel->config[!side].channel_reserve;
struct balance balance;
struct amount_msat owed = view->owed[side];
to_balance(&balance, view->owed[side]);
/* `lowest_splice_amnt` will always be negative or 0 */
if (amount_msat_less_eq_sat(owed, amount_sat(-view->lowest_splice_amnt[side]))) {
status_debug("Relative splice balance invalid");
return false;
}
/* `lowest_splice_amnt` is a relative amount */
if (!amount_msat_sub_sat(&owed, owed,
amount_sat(-view->lowest_splice_amnt[side]))) {
status_debug("Owed amount should not wrap around from splice");
return false;
}
to_balance(&balance, owed);
for (size_t i = 0; i < tal_count(removing); i++)
balance_remove_htlc(&balance, removing[i], side);

View file

@ -76,6 +76,22 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,
u64 commitment_number,
enum side side);
/* Version of `channel_txs` that lets you specify a custom funding outpoint
* and funding_sats.
*/
struct bitcoin_tx **channel_splice_txs(const tal_t *ctx,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
const struct htlc ***htlcmap,
struct wally_tx_output *direct_outputs[NUM_SIDES],
const u8 **funding_wscript,
const struct channel *channel,
const struct pubkey *per_commitment_point,
u64 commitment_number,
enum side side,
s64 splice_amnt,
s64 remote_splice_amnt);
/**
* actual_feerate: what is the actual feerate for the local side.
* @channel: The channel state

42
channeld/inflight.c Normal file
View file

@ -0,0 +1,42 @@
#include "config.h"
#include <assert.h>
#include <bitcoin/psbt.h>
#include <channeld/inflight.h>
#include <wire/wire.h>
struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max)
{
struct inflight *inflight = tal(ctx, struct inflight);
fromwire_bitcoin_outpoint(cursor, max, &inflight->outpoint);
inflight->amnt = fromwire_amount_sat(cursor, max);
inflight->psbt = fromwire_wally_psbt(inflight, cursor, max);
inflight->splice_amnt = fromwire_s64(cursor, max);
inflight->last_tx = fromwire_bitcoin_tx(inflight, cursor, max);
fromwire_bitcoin_signature(cursor, max, &inflight->last_sig);
inflight->i_am_initiator = fromwire_bool(cursor, max);
return inflight;
}
void towire_inflight(u8 **pptr, const struct inflight *inflight)
{
towire_bitcoin_outpoint(pptr, &inflight->outpoint);
towire_amount_sat(pptr, inflight->amnt);
towire_wally_psbt(pptr, inflight->psbt);
towire_s64(pptr, inflight->splice_amnt);
towire_bitcoin_tx(pptr, inflight->last_tx);
towire_bitcoin_signature(pptr, &inflight->last_sig);
towire_bool(pptr, inflight->i_am_initiator);
}
void copy_inflight(struct inflight *dest, struct inflight *src)
{
dest->outpoint = src->outpoint;
dest->amnt = src->amnt;
dest->psbt = src->psbt ? clone_psbt(dest, src->psbt): NULL;
dest->splice_amnt = src->splice_amnt;
dest->last_tx = src->last_tx ? clone_bitcoin_tx(dest, src->last_tx) : NULL;
dest->last_sig = src->last_sig;
dest->i_am_initiator = src->i_am_initiator;
}

24
channeld/inflight.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef LIGHTNING_CHANNELD_INFLIGHT_H
#define LIGHTNING_CHANNELD_INFLIGHT_H
#include "config.h"
#include <bitcoin/tx.h>
#include <common/amount.h>
struct inflight {
struct bitcoin_outpoint outpoint;
struct amount_sat amnt;
struct wally_psbt *psbt;
s64 splice_amnt;
struct bitcoin_tx *last_tx;
/* last_sig is assumed valid if last_tx is set */
struct bitcoin_signature last_sig;
bool i_am_initiator;
};
struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max);
void towire_inflight(u8 **pptr, const struct inflight *inflight);
void copy_inflight(struct inflight *dest, struct inflight *src);
#endif /* LIGHTNING_CHANNELD_INFLIGHT_H */

37
channeld/splice.c Normal file
View file

@ -0,0 +1,37 @@
#include "config.h"
#include <ccan/tal/tal.h>
#include <channeld/splice.h>
struct splice_state *splice_state_new(const tal_t *ctx)
{
struct splice_state *splice_state = tal(ctx, struct splice_state);
splice_state->committed_count = 0;
splice_state->revoked_count = 0;
splice_state->count = 0;
splice_state->locked_ready[LOCAL] = false;
splice_state->locked_ready[REMOTE] = false;
splice_state->await_commitment_succcess = false;
splice_state->inflights = NULL;
return splice_state;
}
struct splice *splice_new(const tal_t *ctx)
{
struct splice *splice = tal(ctx, struct splice);
splice->opener_relative = 0;
splice->accepter_relative = 0;
splice->feerate_per_kw = 0;
splice->force_feerate = false;
splice->force_sign_first = false;
splice->mode = false;
splice->tx_add_input_count = 0;
splice->tx_add_output_count = 0;
splice->current_psbt = NULL;
splice->received_tx_complete = false;
splice->sent_tx_complete = false;
return splice;
}

63
channeld/splice.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef LIGHTNING_CHANNELD_SPLICE_H
#define LIGHTNING_CHANNELD_SPLICE_H
#include "config.h"
#include <bitcoin/short_channel_id.h>
#include <channeld/inflight.h>
#include <common/amount.h>
#include <common/htlc.h>
/* The channel's general splice state for tracking splice candidates */
struct splice_state {
/* The active inflights */
struct inflight **inflights;
/* The pending short channel id for a splice. Set when mutual lock. */
struct short_channel_id short_channel_id;
/* Set to old short channel id when mutual lock occurs. */
struct short_channel_id last_short_channel_id;
/* Tally of which sides are locked, or not */
bool locked_ready[NUM_SIDES];
/* Set to true when commitment cycle completes successfully */
bool await_commitment_succcess;
/* The txid of which splice inflight was confirmed */
struct bitcoin_txid locked_txid;
/* The number of splices that have been signed & committed */
u32 committed_count;
/* the number of splices that have been revoke_and_ack'ed */
u32 revoked_count;
/* The number of splices that are active (awaiting confirmation) */
u32 count;
};
/* Sets `splice_state` items to default values */
struct splice_state *splice_state_new(const tal_t *ctx);
/* An active splice negotiation. Born when splice beings and dies when a splice
* negotation has finished */
struct splice {
/* The opener side's relative balance change */
s64 opener_relative;
/* The accepter side's relative balance change */
s64 accepter_relative;
/* The feerate for the splice (on set for the initiator) */
u32 feerate_per_kw;
/* If the feerate is higher than max, don't abort the splice */
bool force_feerate;
/* Make our side sign first */
bool force_sign_first;
/* After `splice` and `splice_ack` occur, we are in splice mode */
bool mode;
/* Track how many of each tx collab msg we receive */
u16 tx_add_input_count, tx_add_output_count;
/* Current negoitated psbt */
struct wally_psbt *current_psbt;
/* If, in the last splice_update, was tx_complete was received */
bool received_tx_complete;
/* If, in the last splice_update, we sent tx_complete */
bool sent_tx_complete;
};
/* Sets `splice` items to default values */
struct splice *splice_new(const tal_t *ctx);
#endif /* LIGHTNING_CHANNELD_SPLICE_H */

View file

@ -243,6 +243,7 @@ message ListpeersPeersChannelsInflight {
string feerate = 3;
Amount total_funding_msat = 4;
Amount our_funding_msat = 5;
optional sint64 splice_amount = 7;
bytes scratch_txid = 6;
}
@ -1122,6 +1123,7 @@ message ListpeerchannelsChannels {
ONCHAIN = 8;
DUALOPEND_OPEN_INIT = 9;
DUALOPEND_AWAITING_LOCKIN = 10;
CHANNELD_AWAITING_SPLICE = 11;
}
optional bytes peer_id = 1;
optional bool peer_connected = 2;
@ -1186,6 +1188,7 @@ message ListpeerchannelsChannelsInflight {
optional uint32 funding_outnum = 2;
optional string feerate = 3;
optional Amount total_funding_msat = 4;
optional sint64 splice_amount = 7;
optional Amount our_funding_msat = 5;
optional bytes scratch_txid = 6;
}

View file

@ -108,6 +108,7 @@ impl From<responses::ListpeersPeersChannelsInflight> for pb::ListpeersPeersChann
feerate: c.feerate, // Rule #2 for type string
total_funding_msat: Some(c.total_funding_msat.into()), // Rule #2 for type msat
our_funding_msat: Some(c.our_funding_msat.into()), // Rule #2 for type msat
splice_amount: c.splice_amount, // Rule #2 for type integer?
scratch_txid: hex::decode(&c.scratch_txid).unwrap(), // Rule #2 for type txid
}
}
@ -960,6 +961,7 @@ impl From<responses::ListpeerchannelsChannelsInflight> for pb::ListpeerchannelsC
funding_outnum: c.funding_outnum, // Rule #2 for type u32?
feerate: c.feerate, // Rule #2 for type string?
total_funding_msat: c.total_funding_msat.map(|f| f.into()), // Rule #2 for type msat?
splice_amount: c.splice_amount, // Rule #2 for type integer?
our_funding_msat: c.our_funding_msat.map(|f| f.into()), // Rule #2 for type msat?
scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid?
}

8
cln-rpc/src/model.rs generated
View file

@ -1866,6 +1866,8 @@ pub mod responses {
pub feerate: String,
pub total_funding_msat: Amount,
pub our_funding_msat: Amount,
#[serde(skip_serializing_if = "Option::is_none")]
pub splice_amount: Option<i64>,
pub scratch_txid: String,
}
@ -3629,6 +3631,8 @@ pub mod responses {
DUALOPEND_OPEN_INIT,
#[serde(rename = "DUALOPEND_AWAITING_LOCKIN")]
DUALOPEND_AWAITING_LOCKIN,
#[serde(rename = "CHANNELD_AWAITING_SPLICE")]
CHANNELD_AWAITING_SPLICE,
}
impl TryFrom<i32> for ListpeerchannelsChannelsState {
@ -3646,6 +3650,7 @@ pub mod responses {
8 => Ok(ListpeerchannelsChannelsState::ONCHAIN),
9 => Ok(ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT),
10 => Ok(ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN),
11 => Ok(ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE),
o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeerchannelsChannelsState", o)),
}
}
@ -3665,6 +3670,7 @@ pub mod responses {
ListpeerchannelsChannelsState::ONCHAIN => "ONCHAIN",
ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT => "DUALOPEND_OPEN_INIT",
ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN => "DUALOPEND_AWAITING_LOCKIN",
ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE => "CHANNELD_AWAITING_SPLICE",
}.to_string()
}
}
@ -3688,6 +3694,8 @@ pub mod responses {
#[serde(skip_serializing_if = "Option::is_none")]
pub total_funding_msat: Option<Amount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub splice_amount: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub our_funding_msat: Option<Amount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scratch_txid: Option<String>,

View file

@ -37,6 +37,14 @@ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat)
return sat;
}
struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat)
{
struct amount_msat res;
res.millisatoshis = msat.millisatoshis % MSAT_PER_SAT;
return res;
}
/* Different formatting by amounts: btc, sat and msat */
const char *fmt_amount_msat_btc(const tal_t *ctx,
struct amount_msat msat,
@ -360,6 +368,27 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val,
return true;
}
WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val,
struct amount_msat a,
s64 b)
{
if (b < 0)
return amount_msat_sub_sat(val, a, amount_sat(-b));
else
return amount_msat_add_sat(val, a, amount_sat(b));
}
WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val,
struct amount_sat a,
s64 b)
{
if (b < 0)
return amount_sat_sub(val, a, amount_sat(-b));
else
return amount_sat_add(val, a, amount_sat(b));
}
bool amount_sat_eq(struct amount_sat a, struct amount_sat b)
{
return a.satoshis == b.satoshis;

View file

@ -60,6 +60,9 @@ WARN_UNUSED_RESULT bool amount_msat_to_sat(struct amount_sat *sat,
/* You can always truncate millisatoshis->satoshis. */
struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat);
/* The msats truncated by `amount_msat_to_sat_round_down` */
struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat);
/* Simple operations: val = a + b, val = a - b. */
WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val,
struct amount_msat a,
@ -89,6 +92,14 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val,
struct amount_sat sat,
double scale);
WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val,
struct amount_msat a,
s64 b);
WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val,
struct amount_sat a,
s64 b);
struct amount_msat amount_msat_div(struct amount_msat msat, u64 div);
struct amount_sat amount_sat_div(struct amount_sat sat, u64 div);

View file

@ -142,6 +142,10 @@ static const struct feature_style feature_styles[] = {
{ OPT_PROVIDE_PEER_BACKUP_STORAGE,
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
{ OPT_SPLICE,
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} },
};
struct dependency {
@ -467,7 +471,7 @@ const char *feature_name(const tal_t *ctx, size_t f)
"option_trampoline_routing", /* https://github.com/lightning/bolts/pull/836 */
NULL,
NULL, /* 60/61 */
NULL,
"option_splice",
NULL,
NULL,
NULL,

View file

@ -99,7 +99,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx,
#define COMPULSORY_FEATURE(x) ((x) & 0xFFFFFFFE)
#define OPTIONAL_FEATURE(x) ((x) | 1)
/* BOLT #9:
/* BOLT-a526652801a541ed33b34d000a3b686a857c811f #9:
*
* | Bits | Name |...
* | 0/1 | `option_data_loss_protect` |... IN ...
@ -118,6 +118,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx,
* | 26/27 | `option_shutdown_anysegwit` |... IN ...
* | 44/45 | `option_channel_type` |... IN ...
* | 48/49 | `option_payment_metadata` |... 9 ...
* | 62/63 | `option_splice` |... IN ...
*/
#define OPT_DATA_LOSS_PROTECT 0
#define OPT_INITIAL_ROUTING_SYNC 2
@ -135,6 +136,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx,
#define OPT_SHUTDOWN_ANYSEGWIT 26
#define OPT_CHANNEL_TYPE 44
#define OPT_PAYMENT_METADATA 48
#define OPT_SPLICE 62
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9:
* | 28/29 | `option_dual_fund` | ... IN9 ...

View file

@ -7,7 +7,7 @@
* only onion tlv payloads. */
#define ROUTING_MAX_HOPS 20
/* BOLT #7:
/* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7:
*
* The `channel_flags` bitfield...individual bits:
*...

View file

@ -65,6 +65,11 @@ struct channel *new_initial_channel(const tal_t *ctx,
= channel->view[LOCAL].owed[REMOTE]
= remote_msatoshi;
channel->view[LOCAL].lowest_splice_amnt[LOCAL] = 0;
channel->view[LOCAL].lowest_splice_amnt[REMOTE] = 0;
channel->view[REMOTE].lowest_splice_amnt[LOCAL] = 0;
channel->view[REMOTE].lowest_splice_amnt[REMOTE] = 0;
channel->basepoints[LOCAL] = *local_basepoints;
channel->basepoints[REMOTE] = *remote_basepoints;
@ -147,6 +152,34 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx,
return init_tx;
}
const char *channel_update_funding(struct channel *channel,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
s64 splice_amnt)
{
s64 funding_diff = (s64)funding_sats.satoshis - (s64)channel->funding_sats.satoshis; /* Raw: splicing */
s64 remote_splice_amnt = funding_diff - splice_amnt;
channel->funding = *funding;
channel->funding_sats = funding_sats;
if (splice_amnt * 1000 + channel->view[LOCAL].owed[LOCAL].millisatoshis < 0) /* Raw: splicing */
return tal_fmt(tmpctx, "Channel funding update would make local"
" balance negative.");
channel->view[LOCAL].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */
channel->view[REMOTE].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */
if (remote_splice_amnt * 1000 + channel->view[LOCAL].owed[REMOTE].millisatoshis < 0) /* Raw: splicing */
return tal_fmt(tmpctx, "Channel funding update would make"
" remote balance negative.");
channel->view[LOCAL].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */
channel->view[REMOTE].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */
return NULL;
}
u32 channel_feerate(const struct channel *channel, enum side side)
{
return get_feerate(channel->fee_states, channel->opener, side);

View file

@ -16,8 +16,19 @@ struct fulfilled_htlc;
/* View from each side */
struct channel_view {
/* How much is owed to each side (includes pending changes) */
/* How much is owed to each side (includes pending changes).
* The index of `owed` array is always relative to the machine
* this code is running on, so REMOTE is always the other machine
* and LOCAL is always this machine (regardless of view).
*
* For example:
* view[REMOTE].owed[REMOTE] == view[LOCAL].owed[REMOTE]
* view[REMOTE].owed[LOCAL] == view[LOCAL].owed[LOCAL]
*/
struct amount_msat owed[NUM_SIDES];
/* Lowest splice relative change amount of all candidate splices.
* This will be 0 or negative -- never positive. */
s64 lowest_splice_amnt[NUM_SIDES];
};
struct channel {
@ -135,6 +146,17 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx,
struct wally_tx_output *direct_outputs[NUM_SIDES],
char** err_reason);
/* channel_update_funding: Changes the funding for the channel and updates the
* balance by the difference between `old_local_funding_msatoshi` and
* `new_local_funding_msatoshi`.
*
* Returns NULL on success or an error on failure.
*/
const char *channel_update_funding(struct channel *channel,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
s64 splice_amnt);
/**
* channel_feerate: Get fee rate for this side of channel.
* @channel: The channel

View file

@ -503,6 +503,18 @@ struct command_result *param_u64(struct command *cmd, const char *name,
"should be an unsigned 64 bit integer");
}
struct command_result *param_s64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
int64_t **num)
{
*num = tal(cmd, int64_t);
if (json_to_s64(buffer, tok, *num))
return NULL;
return command_fail_badparam(cmd, name, buffer, tok,
"should be an sign 64 bit integer");
}
struct command_result *param_msat(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct amount_msat **msat)

View file

@ -212,6 +212,11 @@ struct command_result *param_u64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
uint64_t **num);
/* Extract number from this (may be a string, or a number literal) */
struct command_result *param_s64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
int64_t **num);
/* Extract msatoshi amount from this string */
struct command_result *param_msat(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,

View file

@ -71,6 +71,8 @@ enum jsonrpc_errcode {
SPLICE_UNKNOWN_CHANNEL = 352,
SPLICE_INVALID_CHANNEL_STATE = 353,
SPLICE_NOT_SUPPORTED = 354,
SPLICE_BUSY_ERROR = 355,
SPLICE_INPUT_ERROR = 356,
/* `connect` errors */
CONNECT_NO_KNOWN_ADDRESS = 400,

View file

@ -165,32 +165,3 @@ psbt_to_witnesses(const tal_t *ctx,
return witnesses;
}
size_t psbt_input_weight(struct wally_psbt *psbt,
size_t in)
{
size_t weight;
const struct wally_map_item *redeem_script;
redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04);
/* txid + txout + sequence */
weight = (32 + 4 + 4) * 4;
if (redeem_script) {
weight +=
(redeem_script->value_len +
(varint_t) varint_size(redeem_script->value_len)) * 4;
} else {
/* zero scriptSig length */
weight += (varint_t) varint_size(0) * 4;
}
return weight;
}
size_t psbt_output_weight(struct wally_psbt *psbt,
size_t outnum)
{
return (8 + psbt->outputs[outnum].script_len +
varint_size(psbt->outputs[outnum].script_len)) * 4;
}

View file

@ -21,7 +21,7 @@ void psbt_finalize_input(const tal_t *ctx,
struct wally_psbt_input *in,
const struct witness *witness);
/* psbt_to_witness_stacks - Take a side's sigs from a PSBT and copy to a
/* psbt_to_witnesses - Take a side's sigs from a PSBT and copy to a
* wire witness
*
* @ctx - allocation context
@ -35,12 +35,4 @@ psbt_to_witnesses(const tal_t *ctx,
enum tx_role side_to_stack,
int input_index_to_ignore);
/* psbt_input_weight - Calculate the tx weight for input index `in` */
size_t psbt_input_weight(struct wally_psbt *psbt,
size_t in);
/* psbt_output_weight - Calculate the tx weight for output index `outnum` */
size_t psbt_output_weight(struct wally_psbt *psbt,
size_t outnum);
#endif /* LIGHTNING_COMMON_PSBT_INTERNAL_H */

View file

@ -45,9 +45,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U
/* Generated stub for towire_node_id */
void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "towire_node_id called!\n"); abort(); }
/* Generated stub for towire_s64 */
void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED)
{ fprintf(stderr, "towire_s64 called!\n"); abort(); }
/* Generated stub for towire_secp256k1_ecdsa_signature */
void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED,
const secp256k1_ecdsa_signature *signature UNNEEDED)
@ -70,6 +67,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED)
/* Generated stub for towire_u64 */
void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED)
{ fprintf(stderr, "towire_u64 called!\n"); abort(); }
/* Generated stub for towire_s64 */
void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED)
{ fprintf(stderr, "towire_s64 called!\n"); abort(); }
/* Generated stub for towire_u8 */
void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
{ fprintf(stderr, "towire_u8 called!\n"); abort(); }

View file

@ -87,6 +87,9 @@ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* Generated stub for fromwire_u64 */
u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); }
/* Generated stub for fromwire_s64 */
s64 fromwire_s64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_s64 called!\n"); abort(); }
/* Generated stub for fromwire_u8 */
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
@ -184,6 +187,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED)
/* Generated stub for towire_u64 */
void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED)
{ fprintf(stderr, "towire_u64 called!\n"); abort(); }
/* Generated stub for towire_s64 */
void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED)
{ fprintf(stderr, "towire_s64 called!\n"); abort(); }
/* Generated stub for towire_u8 */
void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
{ fprintf(stderr, "towire_u8 called!\n"); abort(); }

View file

@ -92,6 +92,9 @@ static bool is_msg_gossip_broadcast(const u8 *cursor)
case WIRE_OPEN_CHANNEL2:
case WIRE_ACCEPT_CHANNEL2:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
break;
}
return false;

View file

@ -95,6 +95,9 @@ static bool public_msg_type(enum peer_wire type)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
return false;
case WIRE_CHANNEL_ANNOUNCEMENT:
case WIRE_NODE_ANNOUNCEMENT:

View file

@ -390,6 +390,9 @@ static bool is_urgent(enum peer_wire type)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
return false;
/* These are time-sensitive, and so send without delay. */

View file

@ -35,6 +35,7 @@ typemap = {
'outputdesc': 'OutputDesc',
'hash': 'Sha256',
'secret': 'Secret',
'integer': 'i64',
}
header = f"""#![allow(non_camel_case_types)]

View file

@ -1212,6 +1212,32 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("openchannel_abort", payload)
def splice_init(self, chan_id, amount, initialpsbt=None, feerate_per_kw=None):
""" Initiate a splice """
payload = {
"channel_id": chan_id,
"relative_amount": amount,
"initialpsbt": initialpsbt,
"feerate_per_kw": feerate_per_kw,
}
return self.call("splice_init", payload)
def splice_update(self, chan_id, psbt):
""" Update a splice """
payload = {
"channel_id": chan_id,
"psbt": psbt
}
return self.call("splice_update", payload)
def splice_signed(self, chan_id, psbt):
""" Initiate a splice """
payload = {
"channel_id": chan_id,
"psbt": psbt
}
return self.call("splice_signed", payload)
def paystatus(self, bolt11=None):
"""Detail status of attempts to pay {bolt11} or any."""
payload = {

File diff suppressed because one or more lines are too long

View file

@ -91,6 +91,7 @@ def listpeers_peers_channels_inflight2py(m):
"feerate": m.feerate, # PrimitiveField in generate_composite
"total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite
"our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite
"splice_amount": m.splice_amount, # PrimitiveField in generate_composite
"scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite
})
@ -749,6 +750,7 @@ def listpeerchannels_channels_inflight2py(m):
"funding_outnum": m.funding_outnum, # PrimitiveField in generate_composite
"feerate": m.feerate, # PrimitiveField in generate_composite
"total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite
"splice_amount": m.splice_amount, # PrimitiveField in generate_composite
"our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite
"scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite
})

View file

@ -80,6 +80,7 @@ SLOW_MACHINE = env("SLOW_MACHINE", "0") == "1"
DEPRECATED_APIS = env("DEPRECATED_APIS", "0") == "1"
TIMEOUT = int(env("TIMEOUT", 180 if SLOW_MACHINE else 60))
EXPERIMENTAL_DUAL_FUND = env("EXPERIMENTAL_DUAL_FUND", "0") == "1"
EXPERIMENTAL_SPLICING = env("EXPERIMENTAL_SPLICING", "0") == "1"
def wait_for(success, timeout=TIMEOUT):
@ -787,6 +788,8 @@ class LightningNode(object):
self.daemon.opts["dev-no-reconnect"] = None
if EXPERIMENTAL_DUAL_FUND:
self.daemon.opts["experimental-dual-fund"] = None
if EXPERIMENTAL_SPLICING:
self.daemon.opts["experimental-splicing"] = None
if options is not None:
self.daemon.opts.update(options)

View file

@ -100,6 +100,7 @@ start_nodes() {
dev-fast-gossip
dev-bitcoind-poll=5
experimental-dual-fund
experimental-splicing
experimental-offers
funder-policy=match
funder-policy-mod=100

View file

@ -81,6 +81,12 @@ void db_bind_u64(struct db_stmt *stmt, u64 val)
stmt->bindings[pos].v.u64 = val;
}
void db_bind_s64(struct db_stmt *stmt, s64 val)
{
u64 uval = val;
db_bind_u64(stmt, uval);
}
void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len)
{
size_t pos = check_bind_pos(stmt);
@ -277,6 +283,11 @@ u64 db_col_u64(struct db_stmt *stmt, const char *colname)
return stmt->db->config->column_u64_fn(stmt, col);
}
u64 db_col_s64(struct db_stmt *stmt, const char *colname)
{
return db_col_u64(stmt, colname);
}
int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def)
{
size_t col = db_query_colnum(stmt, colname);

View file

@ -21,6 +21,7 @@ struct wally_tx;
void db_bind_null(struct db_stmt *stmt);
void db_bind_int(struct db_stmt *stmt, int val);
void db_bind_u64(struct db_stmt *stmt, u64 val);
void db_bind_s64(struct db_stmt *stmt, s64 val);
void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len);
void db_bind_text(struct db_stmt *stmt, const char *val);
void db_bind_preimage(struct db_stmt *stmt, const struct preimage *p);
@ -62,6 +63,7 @@ size_t db_query_colnum(const struct db_stmt *stmt, const char *colname);
int db_col_is_null(struct db_stmt *stmt, const char *colname);
int db_col_int(struct db_stmt *stmt, const char *colname);
u64 db_col_u64(struct db_stmt *stmt, const char *colname);
u64 db_col_s64(struct db_stmt *stmt, const char *colname);
size_t db_col_bytes(struct db_stmt *stmt, const char *colname);
const void* db_col_blob(struct db_stmt *stmt, const char *colname);
char *db_col_strdup(const tal_t *ctx,

View file

@ -59,7 +59,8 @@ TEST_CHECK_DBSTMTS=[0|1] - When running blackbox tests, this will
Note: Only SQLite3.
TEST_DB_PROVIDER=[sqlite3|postgres] - Selects the database to use when running
blackbox tests.
EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests.
EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests.
EXPERIMENTAL_SPLICING=[0|1] - Enable splicing tests.
```
#### Troubleshooting

View file

@ -131,6 +131,7 @@ Core Lightning Documentation
lightning-signinvoice <lightning-signinvoice.7.md>
lightning-signmessage <lightning-signmessage.7.md>
lightning-signpsbt <lightning-signpsbt.7.md>
lightning-splice_init <lightning-splice_init.7.md>
lightning-sql <lightning-sql.7.md>
lightning-staticbackup <lightning-staticbackup.7.md>
lightning-stop <lightning-stop.7.md>

View file

@ -120,6 +120,9 @@ On success, an object is returned, containing:
- **experimental-dual-fund** (object, optional):
- **set** (boolean): `true` if set in config or cmdline
- **source** (string): source of configuration setting
- **experimental-splicing** (object, optional) *(added v23.08)*:
- **set** (boolean): `true` if set in config or cmdline
- **source** (string): source of configuration setting
- **experimental-onion-messages** (object, optional):
- **set** (boolean): `true` if set in config or cmdline
- **source** (string): source of configuration setting
@ -298,6 +301,7 @@ On success, an object is returned, containing:
- **wallet** (string, optional): `wallet` field from config or cmdline default **deprecated, removal in v24.05**
- **large-channels** (boolean, optional): `large-channels` field from config or cmdline, or default **deprecated, removal in v24.05**
- **experimental-dual-fund** (boolean, optional): `experimental-dual-fund` field from config or cmdline, or default **deprecated, removal in v24.05**
- **experimental-splicing** (boolean, optional): `experimental-splicing` field from config or cmdline, or default **deprecated, removal in v24.05**
- **experimental-onion-messages** (boolean, optional): `experimental-onion-messages` field from config or cmdline, or default **deprecated, removal in v24.05**
- **experimental-offers** (boolean, optional): `experimental-offers` field from config or cmdline, or default **deprecated, removal in v24.05**
- **experimental-shutdown-wrong-funding** (boolean, optional): `experimental-shutdown-wrong-funding` field from config or cmdline, or default **deprecated, removal in v24.05**
@ -467,4 +471,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:a40882cad0d889aa736a2932250102be43ae7e62b3d2429b26e0961e4c315f7b)
[comment]: # ( SHA256STAMP:8e7ec36b820cb17ecfc3066802bb07e159fffdd8dfe049d092b8f3b804e05588)

View file

@ -26,7 +26,7 @@ On success, an object containing **channels** is returned. It is an array of ob
- **peer\_id** (pubkey): Node Public key
- **peer\_connected** (boolean): A boolean flag that is set to true if the peer is online
- **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN")
- **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN", "CHANNELD\_AWAITING\_SPLICE")
- **opener** (string): Who initiated the channel (one of "local", "remote")
- **features** (array of strings):
- BOLT #9 features which apply to this channel (one of "option\_static\_remotekey", "option\_anchor\_outputs", "option\_anchors\_zero\_fee\_htlc\_tx", "option\_scid\_alias", "option\_zeroconf")
@ -49,11 +49,12 @@ On success, an object containing **channels** is returned. It is an array of ob
- **last\_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open
- **next\_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open
- **next\_fee\_step** (u32, optional): For inflight opens, the next feerate step we'll use for the channel open
- **inflight** (array of objects, optional): Current candidate funding transactions (only for dual-funding):
- **inflight** (array of objects, optional): Current candidate funding transactions:
- **funding\_txid** (txid): ID of the funding transaction
- **funding\_outnum** (u32): The 0-based output number of the funding transaction which opens the channel
- **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended
- **total\_funding\_msat** (msat): total amount in the channel
- **splice\_amount** (integer): The amouont of sats we're splicing in or out *(added v23.08)*
- **our\_funding\_msat** (msat): amount we have in the channel
- **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now
- **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close
@ -195,4 +196,4 @@ Main web site: <https://github.com/ElementsProject/lightning> Lightning
RFC site (BOLT \#9):
<https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md>
[comment]: # ( SHA256STAMP:c530e39c9144b5fbef0f6474170865095bd7b4c48e6378fdb7f9a7697b909473)
[comment]: # ( SHA256STAMP:dade32248bd309f2514a237cb71be6ddbe12220e1b6899693a032b45b7980a01)

View file

@ -89,6 +89,7 @@ On success, an object containing **peers** is returned. It is an array of objec
- **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended
- **total\_funding\_msat** (msat): total amount in the channel
- **our\_funding\_msat** (msat): amount we have in the channel
- **splice\_amount** (integer): The amouont of sats we're splicing in or out *(added v23.08)*
- **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now
- **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close
- **private** (boolean, optional): if False, we will not announce this channel
@ -398,4 +399,4 @@ Main web site: <https://github.com/ElementsProject/lightning> Lightning
RFC site (BOLT \#9):
<https://github.com/lightning/bolts/blob/master/09-features.md>
[comment]: # ( SHA256STAMP:c0d0cc8f083168fd76caa2430a7c7d27d72a5273c55fb14b0efcbcb7a87274f4)
[comment]: # ( SHA256STAMP:d75b5070288f26a39df39831212f40c397f1389e7c1765f22829d3f3389a56aa)

View file

@ -0,0 +1,82 @@
lightning-splice\_init -- Command to initiate a channel to a peer
=====================================================================
SYNOPSIS
--------
**(WARNING: experimental-splicing only)**
**splice\_init** *channel\_id* *relative\_amount* [*initalpsbt*] [*feerate\_per\_kw*] [*force\_feerate*]
DESCRIPTION
-----------
`splice_init` is a low level RPC command which initiates a channel splice for a
given channel specified by `channel_id`.
*channel\_id* is the channel id of the channel to be spliced.
*relative\_amount* is a positive or negative amount of satoshis to add or
subtract from the channel.
*initalpsbt* is the (optional) base 64 encoded PSBT to begin with. If not
specified, one will be generated automatically.
*feerate\_per\_kw* is the miner fee we promise our peer to pay for our side of
the splice transaction. It is calculated by `feerate_per_kw` *
our\_bytes\_in\_splice\_tx / 1000.
*force\_feerate* is a boolean flag. By default splices will fail if the fee
provided looks too high. This is to protect against accidentally setting your
fee higher than intended. Set `force_feerate` to true to skip this saftey check.
Here is an example set of splice commands that will splice in 100,000 sats to
the first channel that comes out of `listpeerchannels`. The example assumes
you already have at least one confirmed channel.
```shell
RESULT=$(lightning-cli listpeerchannels)
CHANNEL_ID=$(echo $RESULT| jq -r ".channels[0].channel_id")
echo $RESULT
RESULT=$(lightning-cli fundpsbt -k satoshi=100000sat feerate=urgent startweight=800 excess_as_change=true)
INITIALPSBT=$(echo $RESULT | jq -r ".psbt")
echo $RESULT
RESULT=$(lightning-cli splice_init $CHANNEL_ID 100000 $INITIALPSBT)
PSBT=$(echo $RESULT | jq -r ".psbt")
echo $RESULT
RESULT=$(lightning-cli splice_update $CHANNEL_ID $PSBT)
PSBT=$(echo $RESULT | jq -r ".psbt")
echo $RESULT
RESULT=$(lightning-cli signpsbt $PSBT)
PSBT=$(echo $RESULT | jq -r ".signed_psbt")
echo $RESULT
lightning-cli splice_signed $CHANNEL_ID $PSBT
```
RETURN VALUE
------------
[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object is returned, containing:
- **psbt** (string): the (incomplete) PSBT of the splice transaction
[comment]: # (GENERATE-FROM-SCHEMA-END)
SEE ALSO
--------
AUTHOR
------
@dusty\_daemon
RESOURCES
---------
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:40121e2e7b0db8c99de12b4fd086f58f63e0d6643b9da1c1697a34dd5057454e)

View file

@ -271,6 +271,7 @@ The following tables are currently supported:
- `funding_outnum` (type `u32`, sqltype `INTEGER`)
- `feerate` (type `string`, sqltype `TEXT`)
- `total_funding_msat` (type `msat`, sqltype `INTEGER`)
- `splice_amount` (type `integer`, sqltype `INTEGER`)
- `our_funding_msat` (type `msat`, sqltype `INTEGER`)
- `scratch_txid` (type `txid`, sqltype `BLOB`)
- `close_to` (type `hex`, sqltype `BLOB`)
@ -513,4 +514,4 @@ RESOURCES
---------
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:094d94f4d2cfd8e8bdc99be8b692100d3cbd70ab3c297ed8e191c8d4a0f9b6a7)
[comment]: # ( SHA256STAMP:2f77078555f16a9dbee5f068d4d0ba18727aeb378be674cd96bf7c1554a74ce5)

View file

@ -712,6 +712,16 @@ about whether to add funds or not to a proposed channel is handled
automatically by a plugin that implements the appropriate logic for
your needs. The default behavior is to not contribute funds.
* **experimental-splicing**
Specifying this enables support for the splicing protocol ([bolt][bolt] #863),
allowing both parties to dynamically adjust the size a channel. These changes
can be built interactively using PSBT and combined with other channel actions
including dual fund, additional channel splices, or generic transaction activity.
The operations will be bundled into a single transaction. The channel will remain
active while awaiting splice confirmation, however you can only spend the smaller
of the prior channel balance and the new one.
* **experimental-websocket-port**=*PORT*
Specifying this enables support for accepting incoming WebSocket

View file

@ -402,6 +402,25 @@
}
}
},
"experimental-splicing": {
"added": "v23.08",
"type": "object",
"additionalProperties": false,
"required": [
"set",
"source"
],
"properties": {
"set": {
"type": "boolean",
"description": "`true` if set in config or cmdline"
},
"source": {
"type": "string",
"description": "source of configuration setting"
}
}
},
"experimental-onion-messages": {
"type": "object",
"additionalProperties": false,
@ -1449,6 +1468,11 @@
"type": "boolean",
"description": "`experimental-dual-fund` field from config or cmdline, or default"
},
"experimental-splicing": {
"deprecated": "v23.08",
"type": "boolean",
"description": "`experimental-splicing` field from config or cmdline, or default"
},
"experimental-onion-messages": {
"deprecated": "v23.08",
"type": "boolean",

View file

@ -41,7 +41,8 @@
"FUNDING_SPEND_SEEN",
"ONCHAIN",
"DUALOPEND_OPEN_INIT",
"DUALOPEND_AWAITING_LOCKIN"
"DUALOPEND_AWAITING_LOCKIN",
"CHANNELD_AWAITING_SPLICE"
],
"description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally"
},
@ -146,7 +147,7 @@
},
"inflight": {
"type": "array",
"description": "Current candidate funding transactions (only for dual-funding)",
"description": "Current candidate funding transactions",
"items": {
"type": "object",
"additionalProperties": false,
@ -155,6 +156,7 @@
"funding_outnum",
"feerate",
"total_funding_msat",
"splice_amount",
"our_funding_msat",
"scratch_txid"
],
@ -175,6 +177,11 @@
"type": "msat",
"description": "total amount in the channel"
},
"splice_amount": {
"type": "integer",
"added": "v23.08",
"description": "The amouont of sats we're splicing in or out"
},
"our_funding_msat": {
"type": "msat",
"description": "amount we have in the channel"

View file

@ -273,6 +273,7 @@
"feerate",
"total_funding_msat",
"our_funding_msat",
"splice_amount",
"scratch_txid"
],
"properties": {
@ -296,6 +297,11 @@
"type": "msat",
"description": "amount we have in the channel"
},
"splice_amount": {
"type": "integer",
"added": "v23.08",
"description": "The amouont of sats we're splicing in or out"
},
"scratch_txid": {
"type": "txid",
"description": "The commitment transaction txid we would use if we went onchain now"

View file

@ -534,7 +534,7 @@ static u8 *create_unsigned_update(const tal_t *ctx,
/* So valgrind doesn't complain */
memset(&dummy_sig, 0, sizeof(dummy_sig));
/* BOLT #7:
/* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7:
*
* The `channel_flags` bitfield is used to indicate the direction of
* the channel: it identifies the node that this update originated
@ -760,7 +760,7 @@ void refresh_local_channel(struct daemon *daemon,
return;
}
/* BOLT #7:
/* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7:
*
* The `channel_flags` bitfield is used to indicate the direction of
* the channel: it identifies the node that this update originated

View file

@ -641,6 +641,9 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
break;
}

View file

@ -107,7 +107,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client,
return (client->capabilities & HSM_CAP_SIGN_CLOSING_TX) != 0;
case WIRE_HSMD_SIGN_SPLICE_TX:
return (client->capabilities & HSM_CAP_SIGN_CLOSING_TX) != 0;
return (client->capabilities & WIRE_HSMD_SIGN_SPLICE_TX) != 0;
case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER:
return (client->capabilities & HSM_CAP_SIGN_WILL_FUND_OFFER) != 0;
@ -2024,6 +2024,7 @@ u8 *hsmd_init(struct secret hsm_secret,
WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US,
WIRE_HSMD_SIGN_ANCHORSPEND,
WIRE_HSMD_SIGN_HTLC_TX_MINGLE,
WIRE_HSMD_SIGN_SPLICE_TX,
};
/*~ Don't swap this. */

View file

@ -133,7 +133,9 @@ new_inflight(struct channel *channel,
const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt,
const u32 lease_blockheight_start,
const struct amount_msat lease_fee,
const struct amount_sat lease_amt)
const struct amount_sat lease_amt,
s64 splice_amnt,
bool i_am_initiator)
{
struct wally_psbt *last_tx_psbt_clone;
struct channel_inflight *inflight
@ -145,6 +147,7 @@ new_inflight(struct channel *channel,
funding->total_funds = total_funds;
funding->feerate = funding_feerate;
funding->our_funds = our_funds;
funding->splice_amnt = splice_amnt;
inflight->funding = funding;
inflight->channel = channel;
@ -172,6 +175,8 @@ new_inflight(struct channel *channel,
inflight->lease_fee = lease_fee;
inflight->lease_amt = lease_amt;
inflight->i_am_initiator = i_am_initiator;
list_add_tail(&channel->inflights, &inflight->list);
tal_add_destructor(inflight, destroy_inflight);
@ -595,6 +600,11 @@ bool channel_state_awaitish(const struct channel *channel)
|| channel->state == CHANNELD_AWAITING_SPLICE;
}
bool channel_state_closish(enum channel_state channel_state)
{
return channel_state > CHANNELD_NORMAL && channel_state <= CLOSED;
}
struct channel *peer_any_active_channel(struct peer *peer, bool *others)
{
struct channel *channel, *ret = NULL;
@ -808,8 +818,7 @@ void channel_set_state(struct channel *channel,
struct timeabs timestamp;
/* set closer, if known */
if (!(state == CHANNELD_AWAITING_SPLICE)
&& state > CHANNELD_NORMAL && channel->closer == NUM_SIDES) {
if (channel_state_closish(state) && channel->closer == NUM_SIDES) {
if (reason == REASON_LOCAL) channel->closer = LOCAL;
if (reason == REASON_USER) channel->closer = LOCAL;
if (reason == REASON_REMOTE) channel->closer = REMOTE;

View file

@ -25,6 +25,9 @@ struct funding_info {
/* Our original funds, in funding amount */
struct amount_sat our_funds;
/* Relative splicing balance change */
s64 splice_amnt;
};
struct channel_inflight {
@ -57,6 +60,9 @@ struct channel_inflight {
/* Amount requested to lease for this open */
struct amount_sat lease_amt;
/* Did I initate this splice attempt? */
bool i_am_initiator;
};
struct open_attempt {
@ -366,7 +372,9 @@ struct channel_inflight *new_inflight(struct channel *channel,
const u16 lease_chan_max_ppt,
const u32 lease_blockheight_start,
const struct amount_msat lease_fee,
const struct amount_sat lease_amt);
const struct amount_sat lease_amt,
s64 splice_amnt,
bool i_am_initiator);
/* Given a txid, find an inflight channel stub. Returns NULL if none found */
struct channel_inflight *channel_inflight_find(struct channel *channel,
@ -390,6 +398,9 @@ bool channel_state_normalish(const struct channel *channel);
/* Is the channel in AWAITING_*? */
bool channel_state_awaitish(const struct channel *channel);
/* Is the channel in one of the CLOSING or CLOSED like states? */
bool channel_state_closish(enum channel_state channel_state);
void channel_set_owner(struct channel *channel, struct subd *owner);
/* Channel has failed, but can try again. */

File diff suppressed because it is too large Load diff

View file

@ -1186,7 +1186,9 @@ wallet_update_channel(struct lightningd *ld,
channel->lease_chan_max_ppt,
lease_blockheight_start,
channel->push,
lease_amt);
lease_amt,
0,
false);
wallet_inflight_add(ld->wallet, inflight);
return inflight;
@ -1338,7 +1340,9 @@ wallet_commit_channel(struct lightningd *ld,
channel->lease_chan_max_ppt,
lease_blockheight_start,
channel->push,
lease_amt);
lease_amt,
0,
false);
wallet_inflight_add(ld->wallet, inflight);
/* We might have disconnected and decided we didn't need to

View file

@ -166,6 +166,12 @@ struct ext_key *hsm_init(struct lightningd *ld)
fatal("--experimental-anchors needs HSM capable of signing anchors!");
}
if (feature_offered(ld->our_features->bits[INIT_FEATURE],
WIRE_HSMD_SIGN_SPLICE_TX)
&& !hsm_capable(ld, WIRE_HSMD_SIGN_SPLICE_TX)) {
fatal("--experimental-splicing needs HSM capable of signing splices!");
}
/* This is equivalent to makesecret("bolt12-invoice-base") */
msg = towire_hsmd_derive_secret(NULL, tal_dup_arr(tmpctx, u8,
(const u8 *)INVOICE_PATH_BASE_STRING,

View file

@ -217,6 +217,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
list_head_init(&ld->close_commands);
list_head_init(&ld->ping_commands);
list_head_init(&ld->disconnect_commands);
list_head_init(&ld->splice_commands);
list_head_init(&ld->waitblockheight_commands);
list_head_init(&ld->wait_commands);

View file

@ -241,6 +241,9 @@ struct lightningd {
/* Outstanding wait commands */
struct list_head wait_commands;
/* Outstanding splice commands. */
struct list_head splice_commands;
/* Maintained by invoices.c */
struct invoices *invoices;

View file

@ -1188,6 +1188,20 @@ static char *opt_set_dual_fund(struct lightningd *ld)
return NULL;
}
static char *opt_set_splicing(struct lightningd *ld)
{
/* Splicing requires STFU to be enabled */
feature_set_or(ld->our_features,
take(feature_set_for_feature(NULL,
OPTIONAL_FEATURE(OPT_QUIESCE))));
feature_set_or(ld->our_features,
take(feature_set_for_feature(NULL,
OPTIONAL_FEATURE(OPT_SPLICE))));
/* Splicing requires dual-fund to be enabled */
opt_set_dual_fund(ld);
return NULL;
}
static char *opt_set_onion_messages(struct lightningd *ld)
{
feature_set_or(ld->our_features,
@ -1390,6 +1404,11 @@ static void register_opts(struct lightningd *ld)
" and allow peers to establish channels"
" via v2 channel open protocol.");
opt_register_early_noarg("--experimental-splicing",
opt_set_splicing, ld,
"experimental: Enables the ability to resize"
" channels using splicing");
/* This affects our features, so set early. */
opt_register_early_noarg("--experimental-onion-messages",
opt_set_onion_messages, ld,
@ -1890,6 +1909,11 @@ void add_config_deprecated(struct lightningd *ld,
feature_offered(ld->our_features
->bits[INIT_FEATURE],
OPT_DUAL_FUND));
} else if (opt->cb == (void *)opt_set_splicing) {
json_add_bool(response, name0,
feature_offered(ld->our_features
->bits[INIT_FEATURE],
OPT_SPLICE));
} else if (opt->cb == (void *)opt_set_onion_messages) {
json_add_bool(response, name0,
feature_offered(ld->our_features

View file

@ -270,7 +270,7 @@ bool invalid_last_tx(const struct bitcoin_tx *tx)
* 0.7.1 release. */
#ifdef COMPAT_V070
/* Old bug had commitment txs with no outputs; bitcoin_txid asserts. */
return tx->wtx->num_outputs == 0;
return !tx || !tx->wtx || tx->wtx->num_outputs == 0;
#else
return false;
#endif
@ -841,6 +841,9 @@ static void json_add_channel(struct lightningd *ld,
json_add_amount_sat_msat(response,
"our_funding_msat",
inflight->funding->our_funds);
json_add_s64(response,
"splice_amount",
inflight->funding->splice_amnt);
/* Add the expected commitment tx id also */
bitcoin_txid(inflight->last_tx, &txid);
json_add_txid(response, "scratch_txid", &txid);
@ -1764,16 +1767,33 @@ static bool check_funding_tx(const struct bitcoin_tx *tx,
return false;
}
static void update_channel_from_inflight(struct lightningd *ld,
struct channel *channel,
const struct channel_inflight *inflight)
void update_channel_from_inflight(struct lightningd *ld,
struct channel *channel,
const struct channel_inflight *inflight)
{
struct wally_psbt *psbt_copy;
channel->funding = inflight->funding->outpoint;
channel->funding_sats = inflight->funding->total_funds;
channel->our_funds = inflight->funding->our_funds;
if (!amount_sat_add_sat_s64(&channel->our_funds, channel->our_funds,
inflight->funding->splice_amnt)) {
channel_fail_permanent(channel,
REASON_LOCAL,
"Updaing channel view for splice causes"
" an invalid satoshi amount wrapping,"
" channel: %s, initial funds: %s, splice"
" banace change: %s",
type_to_string(tmpctx, struct channel_id,
&channel->cid),
type_to_string(tmpctx, struct amount_sat,
&channel->our_funds),
inflight->funding->splice_amnt);
}
/* Lease infos ! */
channel->lease_expiry = inflight->lease_expiry;
channel->push = inflight->lease_fee;
@ -1829,23 +1849,22 @@ static enum watch_result funding_depth_cb(struct lightningd *ld,
bool min_depth_reached = depth >= channel->minimum_depth;
bool min_depth_no_scid = min_depth_reached && !channel->scid;
bool some_depth_has_scid = depth && channel->scid;
bool some_depth_has_scid = depth != 0 && channel->scid;
/* Reorg can change scid, so always update/save scid when possible (depth=0
* means the stale block with our funding tx was removed) */
if (channel->state != CHANNELD_AWAITING_SPLICE
&& (min_depth_no_scid || some_depth_has_scid)) {
if (min_depth_no_scid || some_depth_has_scid) {
struct txlocator *loc;
struct channel_inflight *inf;
/* Update the channel's info to the correct tx, if needed to
* It's possible an 'inflight' has reached depth */
if (!list_empty(&channel->inflights)) {
if (channel->state != CHANNELD_AWAITING_SPLICE
&& !list_empty(&channel->inflights)) {
inf = channel_inflight_find(channel, txid);
if (!inf) {
channel_fail_permanent(channel,
REASON_LOCAL,
"Txid %s for channel"
log_debug(channel->log,
"Ignoring event for txid %s for channel"
" not found in inflights. (peer %s)",
type_to_string(tmpctx,
struct bitcoin_txid,
@ -1874,8 +1893,9 @@ static enum watch_result funding_depth_cb(struct lightningd *ld,
/* If we restart, we could already have peer->scid from database,
* we don't need to update scid for stub channels(1x1x1) */
if (!channel->scid) {
channel->scid = tal(channel, struct short_channel_id);
if (!channel->scid || channel->state == CHANNELD_AWAITING_SPLICE) {
if(!channel->scid)
channel->scid = tal(channel, struct short_channel_id);
*channel->scid = scid;
wallet_channel_save(ld->wallet, channel);
@ -1926,10 +1946,22 @@ static enum watch_result funding_spent(struct channel *channel,
const struct block *block)
{
struct bitcoin_txid txid;
struct channel_inflight *inflight;
bitcoin_txid(tx, &txid);
wallet_channeltxs_add(channel->peer->ld->wallet, channel,
WIRE_ONCHAIND_INIT, &txid, 0, block->height);
/* If we're doing a splice, we expect the funding transaction to be
* spent, so don't freak out and just keep watching in that case */
list_for_each(&channel->inflights, inflight, list) {
if (bitcoin_txid_eq(&txid,
&inflight->funding->outpoint.txid)) {
return KEEP_WATCHING;
}
}
return onchaind_funding_spent(channel, tx, block->height);
}
@ -1957,7 +1989,7 @@ void channel_watch_funding(struct lightningd *ld, struct channel *channel)
channel_watch_wrong_funding(ld, channel);
}
static void channel_watch_inflight(struct lightningd *ld,
void channel_watch_inflight(struct lightningd *ld,
struct channel *channel,
struct channel_inflight *inflight)
{

View file

@ -114,7 +114,14 @@ void resend_closing_transactions(struct lightningd *ld);
void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative);
void update_channel_from_inflight(struct lightningd *ld,
struct channel *channel,
const struct channel_inflight *inflight);
void channel_watch_funding(struct lightningd *ld, struct channel *channel);
void channel_watch_inflight(struct lightningd *ld,
struct channel *channel,
struct channel_inflight *inflight);
/* If this channel has a "wrong funding" shutdown, watch that too. */
void channel_watch_wrong_funding(struct lightningd *ld, struct channel *channel);

View file

@ -2009,6 +2009,8 @@ static bool peer_save_commitsig_received(struct channel *channel, u64 commitnum,
channel->next_index[LOCAL]++;
/* DTODO: Add inflight_commit_sigs to the DB */
/* Update channel->last_sig and channel->last_tx before saving to db */
channel_set_last_tx(channel, tx, commit_sig);
@ -2022,7 +2024,7 @@ static bool peer_save_commitsig_sent(struct channel *channel, u64 commitnum)
if (commitnum != channel->next_index[REMOTE]) {
channel_internal_error(channel,
"channel_sent_commitsig: expected commitnum %"PRIu64
" got %"PRIu64,
" got %"PRIu64" (while sending commitsig)",
channel->next_index[REMOTE], commitnum);
return false;
}
@ -2252,6 +2254,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg)
struct failed_htlc **failed;
struct changed_htlc *changed;
struct bitcoin_tx *tx;
struct commitsig **inflight_commit_sigs;
struct channel_inflight *inflight;
size_t i;
struct lightningd *ld = channel->peer->ld;
@ -2265,7 +2269,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg)
&fulfilled,
&failed,
&changed,
&tx)
&tx,
&inflight_commit_sigs)
|| !fee_states_valid(fee_states, channel->opener)
|| !height_states_valid(blockheight_states, channel->opener)) {
channel_internal_error(channel,
@ -2296,11 +2301,25 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg)
log_debug(channel->log,
"got commitsig %"PRIu64
": feerate %u, blockheight: %u, %zu added, %zu fulfilled, %zu failed, %zu changed",
": feerate %u, blockheight: %u, %zu added, %zu fulfilled, "
"%zu failed, %zu changed. %zu splice commitments.",
commitnum, get_feerate(fee_states, channel->opener, LOCAL),
get_blockheight(blockheight_states, channel->opener, LOCAL),
tal_count(added), tal_count(fulfilled),
tal_count(failed), tal_count(changed));
tal_count(failed), tal_count(changed),
tal_count(inflight_commit_sigs));
i = 0;
list_for_each(&channel->inflights, inflight, list) {
i++;
}
if (i != tal_count(inflight_commit_sigs)) {
channel_internal_error(channel, "Got commitsig with incorrect "
"number of splice commitments. "
"lightningd expects %zu but got %zu.",
i, tal_count(inflight_commit_sigs));
return;
}
/* New HTLCs */
for (i = 0; i < tal_count(added); i++) {
@ -2347,8 +2366,24 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg)
tal_free(channel->last_htlc_sigs);
channel->last_htlc_sigs = tal_steal(channel, htlc_sigs);
/* Delete all HTLCs and add last_htlc_sigs back in */
wallet_htlc_sigs_save(ld->wallet, channel->dbid,
channel->last_htlc_sigs);
/* Now append htlc sigs for inflights */
i = 0;
list_for_each(&channel->inflights, inflight, list) {
struct commitsig *commit = inflight_commit_sigs[i];
inflight->last_tx = tal_steal(inflight, commit->tx);
inflight->last_tx->chainparams = chainparams;
inflight->last_sig = commit->commit_signature;
wallet_inflight_save(ld->wallet, inflight);
wallet_htlc_sigs_add(ld->wallet, channel->dbid,
inflight->funding->outpoint,
commit->htlc_signatures);
i++;
}
/* Tell it we've committed, and to go ahead with revoke. */
msg = towire_channeld_got_commitsig_reply(msg);

View file

@ -104,14 +104,14 @@ routehint_candidates(const tal_t *ctx,
continue;
}
/* Check channel is in CHANNELD_NORMAL */
/* Check channel is in CHANNELD_NORMAL or CHANNELD_AWAITING_SPLICE */
candidate.c = find_channel_by_scid(peer, &r->short_channel_id);
/* Try seeing if we should be using a remote alias
* instead. The `listpeers` result may have returned
* the REMOTE alias, because it is the only scid we
* have, and it is mandatory once the channel is in
* CHANNELD_NORMAL. */
* CHANNELD_NORMAL or CHANNELD_AWAITING_SPLICE. */
if (!candidate.c)
candidate.c = find_channel_by_alias(peer, &r->short_channel_id, REMOTE);
@ -126,7 +126,8 @@ routehint_candidates(const tal_t *ctx,
continue;
}
if (candidate.c->state != CHANNELD_NORMAL) {
if (candidate.c->state != CHANNELD_NORMAL
&& candidate.c->state != CHANNELD_AWAITING_SPLICE) {
log_debug(ld->log, "%s: abnormal channel",
type_to_string(tmpctx,
struct short_channel_id,

View file

@ -540,6 +540,10 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN
void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_add_s64 */
void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
int64_t value UNNEEDED)
{ fprintf(stderr, "json_add_s64 called!\n"); abort(); }
/* Generated stub for json_add_uncommitted_channel */
void json_add_uncommitted_channel(struct json_stream *response UNNEEDED,
const struct uncommitted_channel *uc UNNEEDED,
@ -790,6 +794,12 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
uint64_t **num UNNEEDED)
{ fprintf(stderr, "param_u64 called!\n"); abort(); }
/* Generated stub for channel_state_normalish */
bool channel_state_normalish(const struct channel *channel UNNEEDED)
{ fprintf(stderr, "channel_state_normalish called!\n"); abort(); }
/* Generated stub for channel_state_awaitish */
bool channel_state_awaitish(const struct channel *channel UNNEEDED)
{ fprintf(stderr, "channel_state_awaitish called!\n"); abort(); }
/* Generated stub for peer_any_active_channel */
struct channel *peer_any_active_channel(struct peer *peer UNNEEDED, bool *others UNNEEDED)
{ fprintf(stderr, "peer_any_active_channel called!\n"); abort(); }

View file

@ -760,14 +760,14 @@ static char *check_balances(const tal_t *ctx,
assert(ok);
initiator_weight +=
psbt_input_weight(psbt, i);
psbt_input_get_weight(psbt, i);
} else {
ok = amount_sat_add(&accepter_inputs,
accepter_inputs, amt);
assert(ok);
accepter_weight +=
psbt_input_weight(psbt, i);
psbt_input_get_weight(psbt, i);
}
}
tot_output_amt = AMOUNT_SAT(0);
@ -826,13 +826,13 @@ static char *check_balances(const tal_t *ctx,
}
initiator_weight +=
psbt_output_weight(psbt, i);
psbt_output_get_weight(psbt, i);
} else {
ok = amount_sat_add(&accepter_outs,
accepter_outs, amt);
assert(ok);
accepter_weight +=
psbt_output_weight(psbt, i);
psbt_output_get_weight(psbt, i);
}
}
@ -997,7 +997,8 @@ static u8 *psbt_to_tx_sigs_msg(const tal_t *ctx,
return towire_tx_signatures(ctx, &state->channel_id,
&state->tx_state->funding.txid,
ws);
ws,
NULL);
}
static void handle_tx_sigs(struct state *state, const u8 *msg)
@ -1009,10 +1010,12 @@ static void handle_tx_sigs(struct state *state, const u8 *msg)
enum tx_role their_role = state->our_role == TX_INITIATOR ?
TX_ACCEPTER : TX_INITIATOR;
struct tlv_txsigs_tlvs *txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx);
if (!fromwire_tx_signatures(tmpctx, msg, &cid, &txid,
cast_const3(
struct witness ***,
&witnesses)))
&witnesses),
&txsig_tlvs))
open_err_fatal(state, "Bad tx_signatures %s",
tal_hex(msg, msg));
@ -1418,6 +1421,9 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
break;
}
@ -1792,6 +1798,9 @@ static bool run_tx_interactive(struct state *state,
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
open_abort(state, "Unexpected wire message %s",
tal_hex(tmpctx, msg));
return false;
@ -1873,7 +1882,7 @@ static u8 *accepter_commits(struct state *state,
struct amount_msat our_msats;
struct channel_id cid;
const u8 *wscript;
u8 *msg;
u8 *msg, *commit_msg;
char *error;
/* Find the funding transaction txid */
@ -1911,9 +1920,12 @@ static u8 *accepter_commits(struct state *state,
}
remote_sig.sighash_type = SIGHASH_ALL;
struct tlv_commitment_signed_tlvs *cs_tlv
= tlv_commitment_signed_tlvs_new(tmpctx);
if (!fromwire_commitment_signed(tmpctx, msg, &cid,
&remote_sig.s,
&htlc_sigs))
&htlc_sigs, &cs_tlv))
open_err_fatal(state, "Parsing commitment signed %s",
tal_hex(tmpctx, msg));
@ -2111,10 +2123,10 @@ static u8 *accepter_commits(struct state *state,
master_badmsg(WIRE_DUALOPEND_SEND_TX_SIGS, msg);
/* Send our commitment sigs over now */
peer_write(state->pps,
take(towire_commitment_signed(NULL,
&state->channel_id,
&local_sig.s, NULL)));
commit_msg = towire_commitment_signed(NULL, &state->channel_id,
&local_sig.s, NULL, NULL);
peer_write(state->pps, take(commit_msg));
tal_free(local_commit);
return msg;
}
@ -2757,7 +2769,7 @@ static u8 *opener_commits(struct state *state,
assert(local_sig.sighash_type == SIGHASH_ALL);
msg = towire_commitment_signed(tmpctx, &state->channel_id,
&local_sig.s,
NULL);
NULL, NULL);
peer_write(state->pps, msg);
peer_billboard(false, "channel open: commitment sent, waiting for reply");
@ -2770,9 +2782,12 @@ static u8 *opener_commits(struct state *state,
}
remote_sig.sighash_type = SIGHASH_ALL;
struct tlv_commitment_signed_tlvs *cs_tlv
= tlv_commitment_signed_tlvs_new(tmpctx);
if (!fromwire_commitment_signed(tmpctx, msg, &cid,
&remote_sig.s,
&htlc_sigs))
&htlc_sigs, &cs_tlv))
open_err_fatal(state, "Parsing commitment signed %s",
tal_hex(tmpctx, msg));
@ -4184,6 +4199,9 @@ static u8 *handle_peer_in(struct state *state)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
break;
}

View file

@ -160,6 +160,9 @@ bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32
/* Generated stub for json_to_u64 */
bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED)
{ fprintf(stderr, "json_to_u64 called!\n"); abort(); }
/* Generated stub for json_to_s64 */
bool json_to_s64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, s64 *num UNNEEDED)
{ fprintf(stderr, "json_to_s64 called!\n"); abort(); }
/* Generated stub for json_tok_bin_from_hex */
u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); }

View file

@ -1016,8 +1016,10 @@ def test_reconnect_remote_sends_no_sigs(node_factory):
l2.daemon.wait_for_logs([r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES',
r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES'])
count = ''.join(l1.daemon.logs).count(r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES')
# l1 only did send them the first time
assert(''.join(l1.daemon.logs).count(r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES') == 1)
assert(count == 1 or count == 2)
@pytest.mark.openchannel('v1')

View file

@ -2225,8 +2225,9 @@ def test_list_features_only(node_factory):
'option_shutdown_anysegwit/odd',
'option_channel_type/odd',
'option_scid_alias/odd',
'option_zeroconf/odd',
'supports_open_accept_channel_type']
'option_zeroconf/odd']
expected += ['supports_open_accept_channel_type']
assert features == expected

View file

@ -3886,6 +3886,8 @@ def test_sql(node_factory, bitcoind):
'type': 'string'},
{'name': 'total_funding_msat',
'type': 'msat'},
{'name': 'splice_amount',
'type': 's64'},
{'name': 'our_funding_msat',
'type': 'msat'},
{'name': 'scratch_txid',
@ -4023,6 +4025,7 @@ def test_sql(node_factory, bitcoind):
'u16': 'INTEGER',
'u32': 'INTEGER',
'u64': 'INTEGER',
's64': 'INTEGER',
'msat': 'INTEGER',
'hex': 'BLOB',
'hash': 'BLOB',
@ -4093,7 +4096,7 @@ def test_sql(node_factory, bitcoind):
assert len(bytes.fromhex(val)) == 32
elif col['type'] == "pubkey":
assert len(bytes.fromhex(val)) == 33
elif col['type'] in ("msat", "integer", "u64", "u32", "u16", "u8", "boolean"):
elif col['type'] in ("msat", "integer", "s64", "u64", "u32", "u16", "u8", "boolean"):
int(val)
elif col['type'] == "number":
float(val)

35
tests/test_splicing.py Normal file
View file

@ -0,0 +1,35 @@
from fixtures import * # noqa: F401,F403
import os
import pytest
import unittest
@unittest.skipIf(os.environ.get("EXPERIMENTAL_SPLICING", '0') != '1', "Need experimental splicing turned on")
@pytest.mark.openchannel('v2')
def test_splice(node_factory, bitcoind):
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True)
chan_id = l1.get_channel_id(l2)
# add extra sats to pay fee
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
result = l1.rpc.splice_update(chan_id, result['psbt'])
result = l1.rpc.signpsbt(result['psbt'])
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
mempool = bitcoind.rpc.getrawmempool(True)
assert len(list(mempool.keys())) == 1
assert result['txid'] in list(mempool.keys())
bitcoind.generate_block(6, wait_for_mempool=1)
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
inv = l2.rpc.invoice(10**2, '3', 'no_3')
l1.rpc.pay(inv['bolt11'])

View file

@ -2,7 +2,7 @@ from pyln.testing.utils import TEST_NETWORK, TIMEOUT, VALGRIND, DEVELOPER, DEPRE
from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce, scid_to_int # noqa: F401
import bitstring
from pyln.client import Millisatoshi
from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND
from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, EXPERIMENTAL_SPLICING
import time
COMPAT = env("COMPAT", "1") == "1"
@ -42,6 +42,10 @@ def expected_peer_features(wumbo_channels=False, extra=[]):
if EXPERIMENTAL_DUAL_FUND:
# option_dual_fund
features += [29]
if EXPERIMENTAL_SPLICING:
features += [29] # option_dual_fund
features += [35] # option_quiesce
features += [63] # option_splice
return hex_bits(features + extra)
@ -55,6 +59,10 @@ def expected_node_features(wumbo_channels=False, extra=[]):
if EXPERIMENTAL_DUAL_FUND:
# option_dual_fund
features += [29]
if EXPERIMENTAL_SPLICING:
features += [29] # option_dual_fund
features += [35] # option_quiesce
features += [63] # option_splice
return hex_bits(features + extra)

View file

@ -231,6 +231,7 @@ class Type(FieldSet):
'failed_htlc',
'existing_htlc',
'simple_htlc',
'inflight',
'utxo',
'bitcoin_tx',
'wirestring',

View file

@ -973,6 +973,8 @@ static struct migration dbmigrations[] = {
/* Splicing requires us to store HTLC sigs for inflight splices and allows us to discard old sigs after splice confirmation. */
{SQL("ALTER TABLE htlc_sigs ADD inflight_tx_id BLOB"), NULL},
{SQL("ALTER TABLE htlc_sigs ADD inflight_tx_outnum INTEGER"), NULL},
{SQL("ALTER TABLE channel_funding_inflights ADD splice_amnt BIGINT DEFAULT 0"), NULL},
{SQL("ALTER TABLE channel_funding_inflights ADD i_am_initiator INTEGER DEFAULT 0"), NULL},
};
/**

View file

@ -172,7 +172,9 @@ struct channel_inflight *new_inflight(struct channel *channel UNNEEDED,
const u16 lease_chan_max_ppt UNNEEDED,
const u32 lease_blockheight_start UNNEEDED,
const struct amount_msat lease_fee UNNEEDED,
const struct amount_sat lease_amt UNNEEDED)
const struct amount_sat lease_amt UNNEEDED,
s64 splice_amnt UNNEEDED,
bool i_am_initiator UNNEEDED)
{ fprintf(stderr, "new_inflight called!\n"); abort(); }
/* Generated stub for new_logger */
struct logger *new_logger(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED,

View file

@ -158,7 +158,7 @@ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED)
bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED)
{ fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); }
/* Generated stub for fromwire_channeld_got_commitsig */
bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED)
bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct commitsig ***inflight_commitsigs UNNEEDED)
{ fprintf(stderr, "fromwire_channeld_got_commitsig called!\n"); abort(); }
/* Generated stub for fromwire_channeld_got_revoke */
bool fromwire_channeld_got_revoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *revokenum UNNEEDED, struct secret *per_commitment_secret UNNEEDED, struct pubkey *next_per_commit_point UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct penalty_base **pbase UNNEEDED, struct bitcoin_tx **penalty_tx UNNEEDED)
@ -350,6 +350,10 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN
void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_add_s64 */
void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
int64_t value UNNEEDED)
{ fprintf(stderr, "json_add_s64 called!\n"); abort(); }
/* Generated stub for json_add_uncommitted_channel */
void json_add_uncommitted_channel(struct json_stream *response UNNEEDED,
const struct uncommitted_channel *uc UNNEEDED,
@ -1713,7 +1717,9 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
sig,
1, lease_commit_sig, 2, 4, 22,
AMOUNT_MSAT(10),
AMOUNT_SAT(1111));
AMOUNT_SAT(1111),
0,
false);
/* do inflights get correctly added to the channel? */
wallet_inflight_add(w, inflight);
@ -1737,7 +1743,9 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
sig,
0, NULL, 0, 0, 0,
AMOUNT_MSAT(0),
AMOUNT_SAT(0));
AMOUNT_SAT(0),
0,
false);
wallet_inflight_add(w, inflight);
CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid),
tal_fmt(w, "Load from DB"));

View file

@ -1150,8 +1150,10 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight)
", lease_blockheight_start"
", lease_fee"
", lease_satoshi"
", splice_amnt"
", i_am_initiator"
") VALUES ("
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
db_bind_u64(stmt, inflight->channel->dbid);
db_bind_txid(stmt, &inflight->funding->outpoint.txid);
@ -1185,6 +1187,9 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight)
db_bind_int(stmt, 0);
}
db_bind_s64(stmt, inflight->funding->splice_amnt);
db_bind_int(stmt, inflight->i_am_initiator);
db_exec_prepared_v2(stmt);
assert(!stmt->error);
tal_free(stmt);
@ -1249,6 +1254,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
struct bitcoin_signature last_sig;
struct bitcoin_tx *last_tx;
struct channel_inflight *inflight;
s64 splice_amnt;
bool i_am_initiator;
secp256k1_ecdsa_signature *lease_commit_sig;
u32 lease_blockheight_start;
@ -1299,6 +1306,9 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
} else
last_tx = NULL;
splice_amnt = db_col_s64(stmt, "splice_amnt");
i_am_initiator = db_col_int(stmt, "i_am_initiator");
inflight = new_inflight(chan, &funding,
db_col_int(stmt, "funding_feerate"),
funding_sat,
@ -1312,7 +1322,9 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
lease_chan_max_ppt,
lease_blockheight_start,
lease_fee,
lease_amt);
lease_amt,
splice_amnt,
i_am_initiator);
/* Pull out the serialized tx-sigs-received-ness */
inflight->remote_tx_sigs = db_col_int(stmt, "funding_tx_remote_sigs_received");
@ -1342,6 +1354,8 @@ static bool wallet_channel_load_inflights(struct wallet *w,
", lease_blockheight_start"
", lease_fee"
", lease_satoshi"
", splice_amnt"
", i_am_initiator"
" FROM channel_funding_inflights"
" WHERE channel_id = ?"
" ORDER BY funding_feerate"));
@ -2064,7 +2078,7 @@ void wallet_announcement_save(struct wallet *w, u64 id,
void wallet_htlcsigs_confirm_inflight(struct wallet *w, struct channel *chan,
struct bitcoin_outpoint confirmed_outpoint)
const struct bitcoin_outpoint *confirmed_outpoint)
{
struct db_stmt *stmt;
@ -2079,15 +2093,15 @@ void wallet_htlcsigs_confirm_inflight(struct wallet *w, struct channel *chan,
" inflight_tx_outnum!=?"
")"
")"));
db_bind_u64(stmt, 0, chan->dbid);
db_bind_txid(stmt, 1, &confirmed_outpoint.txid);
db_bind_int(stmt, 2, confirmed_outpoint.n);
db_bind_u64(stmt, chan->dbid);
db_bind_txid(stmt, &confirmed_outpoint->txid);
db_bind_int(stmt, confirmed_outpoint->n);
db_exec_prepared_v2(take(stmt));
stmt = db_prepare_v2(w->db, SQL("UPDATE htlc_sigs"
" SET inflight_tx_id=NULL"
" WHERE channelid=?"));
db_bind_u64(stmt, 0, chan->dbid);
db_bind_u64(stmt, chan->dbid);
db_exec_prepared_v2(take(stmt));
}
@ -3850,10 +3864,10 @@ void wallet_htlc_sigs_add(struct wallet *w, u64 channel_id,
SQL("INSERT INTO htlc_sigs (channelid,"
" inflight_tx_id, inflight_tx_outnum,"
" signature) VALUES (?, ?, ?)"));
db_bind_u64(stmt, 0, channel_id);
db_bind_txid(stmt, 1, &inflight_outpoint.txid);
db_bind_int(stmt, 2, inflight_outpoint.n);
db_bind_signature(stmt, 3, &htlc_sigs[i].s);
db_bind_u64(stmt, channel_id);
db_bind_txid(stmt, &inflight_outpoint.txid);
db_bind_int(stmt, inflight_outpoint.n);
db_bind_signature(stmt, &htlc_sigs[i].s);
db_exec_prepared_v2(take(stmt));
}
}

View file

@ -608,7 +608,7 @@ bool wallet_shachain_add_hash(struct wallet *wallet,
u64 wallet_get_channel_dbid(struct wallet *wallet);
void wallet_htlcsigs_confirm_inflight(struct wallet *w, struct channel *chan,
struct bitcoin_outpoint confirmed_outpoint);
const struct bitcoin_outpoint *confirmed_outpoint);
/**
* wallet_channel_save -- Upsert the channel into the database
@ -638,9 +638,6 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight);
void wallet_inflight_save(struct wallet *w,
struct channel_inflight *inflight);
void wallet_promote_splice_candidate(struct wallet *w,
struct channel *chan);
/**
* Remove all the inflights from a channel. Also cleans up
* the channel's inflight list

View file

@ -0,0 +1,18 @@
--- wire/peer_exp_wire.csv 2022-06-22 19:07:24.000000000 -0500
+++ - 2022-06-30 16:00:51.000000000 -0500
@@ -65,12 +57,15 @@
msgdata,tx_signatures,txid,sha256,
msgdata,tx_signatures,num_witnesses,u16,
msgdata,tx_signatures,witness_stack,witness_stack,num_witnesses
+msgdata,tx_signatures,tlvs,txsigs_tlvs,
subtype,witness_stack
subtypedata,witness_stack,num_input_witness,u16,
subtypedata,witness_stack,witness_element,witness_element,num_input_witness
subtype,witness_element
subtypedata,witness_element,len,u16,
subtypedata,witness_element,witness,byte,len
+tlvtype,txsigs_tlvs,funding_outpoint_sig,0
+tlvdata,txsigs_tlvs,funding_outpoint_sig,sig,byte,...
msgtype,tx_init_rbf,72
msgdata,tx_init_rbf,channel_id,channel_id,
msgdata,tx_init_rbf,locktime,u32,

View file

@ -1,10 +1,12 @@
--- wire/peer_wire.csv 2021-05-09 15:44:59.166135652 +0930
+++ wire/peer_wire.csv.raw 2021-05-11 09:59:31.695459756 +0930
@@ -244,6 +140,15 @@
@@ -244,6 +140,17 @@
msgdata,channel_reestablish,next_revocation_number,u64,
msgdata,channel_reestablish,your_last_per_commitment_secret,byte,32
msgdata,channel_reestablish,my_current_per_commitment_point,point,
+msgdata,channel_reestablish,tlvs,channel_reestablish_tlvs,
+tlvtype,channel_reestablish_tlvs,next_funding,0
+tlvdata,channel_reestablish_tlvs,next_funding,next_funding_txid,sha256,
+tlvtype,channel_reestablish_tlvs,next_to_send,1
+tlvdata,channel_reestablish_tlvs,next_to_send,commitment_number,tu64,
+tlvtype,channel_reestablish_tlvs,desired_channel_type,3

View file

@ -280,3 +280,4 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max,
{
fromwire(cursor, max, seed, sizeof(*seed));
}

View file

@ -50,6 +50,9 @@ static bool unknown_type(enum peer_wire t)
case WIRE_OPEN_CHANNEL2:
case WIRE_ACCEPT_CHANNEL2:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
return false;
}
return true;
@ -104,6 +107,9 @@ bool is_msg_for_gossipd(const u8 *cursor)
case WIRE_PEER_STORAGE:
case WIRE_YOUR_PEER_STORAGE:
case WIRE_STFU:
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
break;
}
return false;
@ -365,6 +371,30 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id)
* 2. data:
* * [`channel_id`:`channel_id`]
*/
case WIRE_SPLICE:
/* BOLT-splice #2:
* 1. type: 74 (`splice`)
* 2. data:
* * [`chain_hash`:`chain_hash`]
* * [`channel_id`:`channel_id`]
* * [`u32`:`funding_feerate_perkw`]
* * [`point`:`funding_pubkey`]
*/
case WIRE_SPLICE_ACK:
/* BOLT-splice #2:
* 1. type: 76 (`splice_ack`)
* 2. data:
* * [`chain_hash`:`chain_hash`]
* * [`channel_id`:`channel_id`]
* * [`point`:`funding_pubkey`]
*/
case WIRE_SPLICE_LOCKED:
/* BOLT-splice #2:
* 1. type: 78 (`splice_locked`)
* 2. data:
* * [`chain_hash`:`chain_hash`]
* * [`channel_id`:`channel_id`]
*/
return fromwire_channel_id(&cursor, &max, channel_id);
}
return false;

View file

@ -66,6 +66,9 @@ msgdata,tx_signatures,witnesses,witness,num_witnesses
subtype,witness
subtypedata,witness,len,u16,
subtypedata,witness,witness_data,byte,len
msgdata,tx_signatures,tlvs,txsigs_tlvs,
tlvtype,txsigs_tlvs,funding_outpoint_sig,0
tlvdata,txsigs_tlvs,funding_outpoint_sig,sig,byte,...
msgtype,tx_init_rbf,72
msgdata,tx_init_rbf,channel_id,channel_id,
msgdata,tx_init_rbf,locktime,u32,
@ -203,6 +206,20 @@ subtypedata,lease_rates,channel_fee_max_base_msat,tu32,
msgtype,stfu,2
msgdata,stfu,channel_id,channel_id,
msgdata,stfu,initiator,u8,
msgtype,splice,75
msgdata,splice,channel_id,channel_id,
msgdata,splice,chain_hash,chain_hash,
msgdata,splice,relative_satoshis,s64,
msgdata,splice,funding_feerate_perkw,u32,
msgdata,splice,locktime,u32,
msgdata,splice,funding_pubkey,point,
msgtype,splice_ack,76
msgdata,splice_ack,channel_id,channel_id,
msgdata,splice_ack,chain_hash,chain_hash,
msgdata,splice_ack,relative_satoshis,s64,
msgdata,splice_ack,funding_pubkey,point,
msgtype,splice_locked,77,
msgdata,splice_locked,channel_id,channel_id,
msgtype,shutdown,38
msgdata,shutdown,channel_id,channel_id,
msgdata,shutdown,len,u16,
@ -247,6 +264,9 @@ msgdata,commitment_signed,channel_id,channel_id,
msgdata,commitment_signed,signature,signature,
msgdata,commitment_signed,num_htlcs,u16,
msgdata,commitment_signed,htlc_signature,signature,num_htlcs
msgdata,commitment_signed,splice_channel_id,commitment_signed_tlvs,
tlvtype,commitment_signed_tlvs,splice_info,0
tlvdata,commitment_signed_tlvs,splice_info,splice_channel_id,channel_id,
msgtype,revoke_and_ack,133
msgdata,revoke_and_ack,channel_id,channel_id,
msgdata,revoke_and_ack,per_commitment_secret,byte,32

1 msgtype,init,16
66 subtype,witness
67 subtypedata,witness,len,u16,
68 subtypedata,witness,witness_data,byte,len
69 msgdata,tx_signatures,tlvs,txsigs_tlvs,
70 tlvtype,txsigs_tlvs,funding_outpoint_sig,0
71 tlvdata,txsigs_tlvs,funding_outpoint_sig,sig,byte,...
72 msgtype,tx_init_rbf,72
73 msgdata,tx_init_rbf,channel_id,channel_id,
74 msgdata,tx_init_rbf,locktime,u32,
206 msgtype,stfu,2
207 msgdata,stfu,channel_id,channel_id,
208 msgdata,stfu,initiator,u8,
209 msgtype,splice,75
210 msgdata,splice,channel_id,channel_id,
211 msgdata,splice,chain_hash,chain_hash,
212 msgdata,splice,relative_satoshis,s64,
213 msgdata,splice,funding_feerate_perkw,u32,
214 msgdata,splice,locktime,u32,
215 msgdata,splice,funding_pubkey,point,
216 msgtype,splice_ack,76
217 msgdata,splice_ack,channel_id,channel_id,
218 msgdata,splice_ack,chain_hash,chain_hash,
219 msgdata,splice_ack,relative_satoshis,s64,
220 msgdata,splice_ack,funding_pubkey,point,
221 msgtype,splice_locked,77,
222 msgdata,splice_locked,channel_id,channel_id,
223 msgtype,shutdown,38
224 msgdata,shutdown,channel_id,channel_id,
225 msgdata,shutdown,len,u16,
264 msgdata,commitment_signed,signature,signature,
265 msgdata,commitment_signed,num_htlcs,u16,
266 msgdata,commitment_signed,htlc_signature,signature,num_htlcs
267 msgdata,commitment_signed,splice_channel_id,commitment_signed_tlvs,
268 tlvtype,commitment_signed_tlvs,splice_info,0
269 tlvdata,commitment_signed_tlvs,splice_info,splice_channel_id,channel_id,
270 msgtype,revoke_and_ack,133
271 msgdata,revoke_and_ack,channel_id,channel_id,
272 msgdata,revoke_and_ack,per_commitment_secret,byte,32

View file

@ -40,6 +40,11 @@ static void set_scid(struct short_channel_id *scid)
memset(scid, 2, sizeof(struct short_channel_id));
}
static void set_cid(struct channel_id *cid)
{
memset(cid, 2, sizeof(struct channel_id));
}
/* Size up to field. */
#define upto_field(p, field) \
((char *)&(p)->field - (char *)(p))
@ -160,6 +165,7 @@ struct msg_commitment_signed {
struct channel_id channel_id;
secp256k1_ecdsa_signature signature;
secp256k1_ecdsa_signature *htlc_signature;
struct tlv_commitment_signed_tlvs *tlvs;
};
struct msg_node_announcement {
secp256k1_ecdsa_signature signature;
@ -525,17 +531,20 @@ static void *towire_struct_commitment_signed(const tal_t *ctx,
return towire_commitment_signed(ctx,
&s->channel_id,
&s->signature,
s->htlc_signature);
s->htlc_signature,
s->tlvs);
}
static struct msg_commitment_signed *fromwire_struct_commitment_signed(const tal_t *ctx, const void *p)
{
struct msg_commitment_signed *s = tal(ctx, struct msg_commitment_signed);
s->tlvs = tlv_commitment_signed_tlvs_new(ctx);
if (!fromwire_commitment_signed(s, p,
&s->channel_id,
&s->signature,
&s->htlc_signature))
&s->htlc_signature,
&s->tlvs))
return tal_free(s);
return s;
}
@ -781,7 +790,8 @@ static bool commitment_signed_eq(const struct msg_commitment_signed *a,
const struct msg_commitment_signed *b)
{
return eq_upto(a, b, htlc_signature)
&& eq_var(a, b, htlc_signature);
&& eq_var(a, b, htlc_signature)
&& eq_tlv(a, b, splice_info, channel_id_eq);
}
static bool funding_signed_eq(const struct msg_funding_signed *a,
@ -1007,11 +1017,14 @@ int main(int argc, char *argv[])
memset(&cs, 2, sizeof(cs));
cs.htlc_signature = tal_arr(ctx, secp256k1_ecdsa_signature, 2);
memset(cs.htlc_signature, 2, sizeof(secp256k1_ecdsa_signature)*2);
cs.tlvs = tlv_commitment_signed_tlvs_new(tmpctx);
cs.tlvs->splice_info = tal(ctx, struct channel_id);
set_cid(cs.tlvs->splice_info);
msg = towire_struct_commitment_signed(ctx, &cs);
cs2 = fromwire_struct_commitment_signed(ctx, msg);
assert(commitment_signed_eq(&cs, cs2));
test_corruption(&cs, cs2, commitment_signed);
test_corruption_tlv(&cs, cs2, commitment_signed);
memset(&fs, 2, sizeof(fs));

View file

@ -4,6 +4,7 @@
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/endian/endian.h>
#include <ccan/mem/mem.h>
#include <channeld/inflight.h>
#include <common/utils.h>
void towire(u8 **pptr, const void *data, size_t len)