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

@@ -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(); }