splice: Add plugin for magic “splice all” command

The command called “splice” can take a json payload or a ‘splice script’, process it into a list of ‘actions’ and then execute those actions.

These actions include or will include everything you would want to do with a splice:
* Splice into a channel
* Splice out of a channel
* Fund from wallet
* Deposit to wallet
* Send funds to bitcoin address

Changelog-Added: A new magic “splice” command is added that can take a ‘splice script’ or json payload and perform any complex splice across multiple channels merging the result into a single transaction. Some features are disabled and will be added in time.
This commit is contained in:
Dusty Daemon 2024-11-11 13:19:24 +10:30 committed by Rusty Russell
parent 7ce0dc6e2e
commit 7fd16dc493
9 changed files with 1627 additions and 2 deletions

View File

@ -36,3 +36,79 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx,
return out;
}
static const char *segwit_addr_net_decode(int *witness_version,
uint8_t *witness_program,
size_t *witness_program_len,
const char *addrz,
const struct chainparams *chainparams)
{
if (segwit_addr_decode(witness_version, witness_program,
witness_program_len, chainparams->onchain_hrp,
addrz))
return chainparams->onchain_hrp;
else
return NULL;
}
bool decode_scriptpubkey_from_addr(const tal_t *ctx,
const struct chainparams *chainparams,
const char *address,
u8 **scriptpubkey)
{
struct bitcoin_address destination;
int witness_version;
/* segwit_addr_net_decode requires a buffer of size 40, and will
* not write to the buffer if the address is too long, so a buffer
* of fixed size 40 will not overflow. */
uint8_t witness_program[40];
size_t witness_program_len;
const char *bech32;
u8 addr_version;
if (ripemd160_from_base58(&addr_version, &destination.addr,
address, strlen(address))) {
if (addr_version == chainparams->p2pkh_version) {
*scriptpubkey = scriptpubkey_p2pkh(ctx, &destination);
return true;
} else if (addr_version == chainparams->p2sh_version) {
*scriptpubkey =
scriptpubkey_p2sh_hash(ctx, &destination.addr);
return true;
} else {
return false;
}
/* Insert other parsers that accept pointer+len here. */
return false;
}
bech32 = segwit_addr_net_decode(&witness_version, witness_program,
&witness_program_len, address,
chainparams);
if (bech32) {
bool witness_ok;
if (witness_version == 0) {
witness_ok = (witness_program_len == 20 ||
witness_program_len == 32);
} else if (witness_version == 1) {
witness_ok = (witness_program_len == 32);
} else {
witness_ok = true;
}
if (!witness_ok)
return false;
if (!streq(bech32, chainparams->onchain_hrp))
return false;
*scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version,
witness_program,
witness_program_len);
return true;
}
/* Insert other parsers that accept null-terminated string here. */
return false;
}

View File

@ -8,4 +8,9 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx,
const struct chainparams *chainparams,
const u8 *scriptpubkey);
bool decode_scriptpubkey_from_addr(const tal_t *ctx,
const struct chainparams *chainparams,
const char *address,
u8 **scriptpubkey);
#endif /* LIGHTNING_COMMON_ADDR_H */

View File

@ -43,4 +43,7 @@ char *fmt_channel_id(const tal_t *ctx, const struct channel_id *channel_id);
void towire_channel_id(u8 **pptr, const struct channel_id *channel_id);
bool fromwire_channel_id(const u8 **cursor, size_t *max,
struct channel_id *channel_id);
char *fmt_channel_id(const tal_t *ctx, const struct channel_id *channel_id);
#endif /* LIGHTNING_COMMON_CHANNEL_ID_H */

View File

@ -465,6 +465,22 @@ struct command_result *param_string(struct command *cmd, const char *name,
return NULL;
}
/* Extract a string or a json array */
struct command_result *param_string_or_array(struct command *cmd, const char *name,
const char * buffer, const jsmntok_t *tok,
struct str_or_arr **result)
{
*result = tal(cmd, struct str_or_arr);
(*result)->arr = NULL;
(*result)->str = NULL;
if (tok->type == JSMN_ARRAY) {
(*result)->arr = tok;
return NULL;
}
return param_string(cmd, name, buffer, tok, &(*result)->str);
}
struct command_result *param_invstring(struct command *cmd, const char *name,
const char * buffer, const jsmntok_t *tok,
const char **str)

View File

@ -198,6 +198,17 @@ struct command_result *param_string(struct command *cmd, const char *name,
const char * buffer, const jsmntok_t *tok,
const char **str);
struct str_or_arr
{
const char *str;
const jsmntok_t *arr;
};
/* Extract a string or a json array */
struct command_result *param_string_or_array(struct command *cmd, const char *name,
const char * buffer, const jsmntok_t *tok,
struct str_or_arr **result);
/* Extract an invoice string from a generic string, strip the `lightning:`
* prefix from it if needed. */
struct command_result *param_invstring(struct command *cmd, const char *name,

View File

@ -50,13 +50,15 @@ PLUGIN_SPENDER_SRC := \
plugins/spender/main.c \
plugins/spender/multifundchannel.c \
plugins/spender/multiwithdraw.c \
plugins/spender/openchannel.c
plugins/spender/openchannel.c \
plugins/spender/splice.c
PLUGIN_SPENDER_HEADER := \
plugins/spender/multifundchannel.h \
plugins/spender/multiwithdraw.h \
plugins/spender/fundchannel.h \
plugins/spender/multifundchannel.h \
plugins/spender/openchannel.h
plugins/spender/openchannel.h \
plugins/spender/splice.h
PLUGIN_SPENDER_OBJS := $(PLUGIN_SPENDER_SRC:.c=.o)
PLUGIN_RECOVER_SRC := plugins/recover.c
@ -153,6 +155,7 @@ PLUGIN_COMMON_OBJS := \
bitcoin/signature.o \
bitcoin/tx.o \
bitcoin/varint.o \
common/addr.o \
common/amount.o \
common/autodata.o \
common/coin_mvt.o \
@ -179,6 +182,7 @@ PLUGIN_COMMON_OBJS := \
common/psbt_open.o \
common/pseudorand.o \
common/random_select.o \
common/splice_script.o \
common/setup.o \
common/status_levels.o \
common/utils.o \

View File

@ -3,6 +3,7 @@
#include <plugins/spender/multifundchannel.h>
#include <plugins/spender/multiwithdraw.h>
#include <plugins/spender/openchannel.h>
#include <plugins/spender/splice.h>
/*~ The spender plugin contains various commands that handle
* spending from the onchain wallet. */
@ -27,6 +28,7 @@ int main(int argc, char **argv)
tal_expand(&commands, multiwithdraw_commands, num_multiwithdraw_commands);
tal_expand(&commands, fundchannel_commands, num_fundchannel_commands);
tal_expand(&commands, multifundchannel_commands, num_multifundchannel_commands);
tal_expand(&commands, splice_commands, num_splice_commands);
/* tal_expand(&commands, whatever_commands, num_whatever_commands); */
notifs = tal_arr(NULL, struct plugin_notification, 0);

1449
plugins/spender/splice.c Normal file

File diff suppressed because it is too large Load Diff

59
plugins/spender/splice.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef LIGHTNING_PLUGINS_SPENDER_SPLICE_H
#define LIGHTNING_PLUGINS_SPENDER_SPLICE_H
#include "config.h"
#include <plugins/libplugin.h>
extern const struct plugin_command splice_commands[];
extern const size_t num_splice_commands;
enum splice_cmd_state {
SPLICE_CMD_NONE = 0,
SPLICE_CMD_INIT,
SPLICE_CMD_UPDATE,
SPLICE_CMD_UPDATE_NEEDS_CHANGES,
SPLICE_CMD_UPDATE_DONE,
SPLICE_CMD_RECVED_SIGS,
SPLICE_CMD_DONE,
};
struct splice_cmd_action_state {
enum splice_cmd_state state;
};
struct splice_cmd {
/* The plugin-level command. */
struct command *cmd;
/* Script input by user */
const char *script;
/* The result of parsing the script or json */
struct splice_script_result **actions;
/* The states of actions at the same index */
struct splice_cmd_action_state **states;
/* The active psbt */
struct wally_psbt *psbt;
/* Output result but don't do any action */
bool dryrun;
/* Execute the splice and abort at the last moment */
bool wetrun;
/* Feerate queried from lightningd */
u32 feerate_per_kw;
/* Override max feerate */
bool force_feerate;
/* How many wallet inputs have we added to the psbt */
int wallet_inputs_to_signed;
/* Final result */
struct bitcoin_txid final_txid;
/* Has the fee been calculated yet */
bool fee_calculated;
/* The amount of sats provided by the user in the inital psbt */
struct amount_sat initial_funds;
/* The minimum sats that must go back into the wallet */
struct amount_sat emergency_sat;
/* A verbose debug log of all the splice states */
char *debug_log;
/* Counter used for more readable debug logs */
int debug_counter;
};
#endif /* LIGHTNING_PLUGINS_SPENDER_SPLICE_H */