Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splicing support #95

Draft
wants to merge 13 commits into
base: 2023-08-remote-hsmd-v23.08
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,44 @@ static bool channel_announcement_negotiate(struct peer *peer)
return sent_announcement;
}

static void lock_signer_outpoint(const struct bitcoin_outpoint *outpoint) {
const u8 *msg;
bool is_buried = false;

do {
/* Make sure the hsmd agrees that this outpoint is
* sufficiently buried. */
msg = towire_hsmd_check_outpoint(NULL, &outpoint->txid, outpoint->n);
msg = hsm_req(tmpctx, take(msg));
if (!fromwire_hsmd_check_outpoint_reply(msg, &is_buried))
status_failed(STATUS_FAIL_HSM_IO,
"Bad hsmd_check_outpoint_reply: %s",
tal_hex(tmpctx, msg));

/* the signer should have a shorter buried height requirement so
* it almost always will be ready ahead of us.*/
if (!is_buried) {
sleep(10);
}
} while (!is_buried);

/* tell the signer that we are now locked */
msg = towire_hsmd_lock_outpoint(NULL, &outpoint->txid, outpoint->n);
msg = hsm_req(tmpctx, take(msg));
if (!fromwire_hsmd_lock_outpoint_reply(msg))
status_failed(STATUS_FAIL_HSM_IO,
"Bad hsmd_lock_outpoint_reply: %s",
tal_hex(tmpctx, msg));
}

/* Call this method when channel_ready status are changed. */
static void check_mutual_channel_ready(const struct peer *peer)
{
if (peer->channel_ready[LOCAL] && peer->channel_ready[REMOTE]) {
lock_signer_outpoint(&peer->channel->funding);
}
}

/* Call this method when splice_locked status are changed. If both sides have
* splice_locked'ed than this function consumes the `splice_locked_ready` values
* and considers the channel funding to be switched to the splice tx. */
Expand Down Expand Up @@ -751,6 +789,9 @@ static void check_mutual_splice_locked(struct peer *peer)
status_debug("mutual splice_locked, channel updated to: %s",
type_to_string(tmpctx, struct channel, peer->channel));

/* ensure the signer is locking at the same time */
lock_signer_outpoint(&inflight->outpoint);

msg = towire_channeld_got_splice_locked(NULL, inflight->amnt,
inflight->splice_amnt,
&inflight->outpoint.txid);
Expand Down Expand Up @@ -831,6 +872,7 @@ static void handle_peer_channel_ready(struct peer *peer, const u8 *msg)

peer->tx_sigs_allowed = false;
peer->channel_ready[REMOTE] = true;
check_mutual_channel_ready(peer);
if (tlvs->short_channel_id != NULL) {
status_debug(
"Peer told us that they'll use alias=%s for this channel",
Expand Down Expand Up @@ -2918,6 +2960,26 @@ static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt)
return weight;
}

/* Get the non-initiator (acceptor) amount after the splice */
static struct amount_msat splice_acceptor_balance(struct peer *peer,
enum tx_role our_role)
{
struct amount_msat acceptor_value;

/* Start with the acceptor's balance before the splice */
acceptor_value =
peer->channel->view->owed[our_role == TX_INITIATOR ? REMOTE : LOCAL];

/* Adjust by the acceptor's relative splice amount (signed) */
if (!amount_msat_add_sat_s64(&acceptor_value, acceptor_value,
peer->splicing->accepter_relative))
peer_failed_warn(
peer->pps, &peer->channel_id,
"Unable to add accepter's relative splice to prior balance.");

return acceptor_value;
}

/* Returns the total channel funding output amount if all checks pass.
* Otherwise, exits via peer_failed_warn. DTODO: Change to `tx_abort`. */
static struct amount_sat check_balances(struct peer *peer,
Expand Down Expand Up @@ -3450,6 +3512,39 @@ static struct inflight *inflights_new(struct peer *peer)
return inf;
}

static void update_hsmd_with_splice(struct peer *peer,
struct inflight *inflight,
const struct amount_msat push_val)
{
u8 *msg;

/* local_upfront_shutdown_script, local_upfront_shutdown_wallet_index,
* remote_upfront_shutdown_script aren't allowed to change, so we
* don't need to gather them */
msg = towire_hsmd_setup_channel(
NULL,
peer->channel->opener == LOCAL,
inflight->amnt,
push_val,
&inflight->outpoint.txid,
inflight->outpoint.n,
peer->channel->config[LOCAL].to_self_delay,
/*local_upfront_shutdown_script*/ NULL,
/*local_upfront_shutdown_wallet_index*/ NULL,
&peer->channel->basepoints[REMOTE],
&peer->channel->funding_pubkey[REMOTE],
Copy link
Collaborator

@devrandom devrandom Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the funding pubkey is allowed to vary in the splice, and we need to be involved in that. I'm wondering where that happens and how we will be informed of the remote one (and asked about the local one).

(note that the funding keys will be the same for all splice candidates)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to be involved in that

Are you saying that roundtrips/protocol is missing?

how we will be informed of the remote one

I think we are being informed with this call. Am I missing something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funding pubkey is allowed to vary in the splice

https://github.com/lightning-signer/c-lightning/blob/2023-08-explore-splicing/channeld/channeld.c#L3536-L3539

That seems to imply it is not changing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

each of the prior is one side, presume there are similar in the other "direction"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the spec allows for it to change (for the whole splice, not per RBF candidate). we should ask the CLN team about this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just scoured the spec looking for where it is allowed to change ... can you point me to it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/lightning/bolts/pull/863/files#diff-ed04ca2c673fd6aabde69389511fa9ee60cb44d6b2ef6c88b549ffaa753d6afeR522

    type: 74 (splice)

    data:
        [chain_hash:chain_hash]
        [channel_id:channel_id]
        [u64:funding_satoshis]
        [u32:funding_feerate_perkw]
        [u32:locktime]
        [point:funding_pubkey]

    type: 76 (splice_ack)

    data:
        [chain_hash:chain_hash]
        [channel_id:channel_id]
        [u64:funding_satoshis]
        [point:funding_pubkey]

Copy link
Collaborator

@devrandom devrandom Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like CLN explicitly doesn't support changing them (they send an error to the peer if the key changes), but I assume that will be fixed before release. need to ask them.

peer->channel->config[REMOTE].to_self_delay,
/*remote_upfront_shutdown_script*/ NULL,
peer->channel->type);

wire_sync_write(HSM_FD, take(msg));
msg = wire_sync_read(tmpctx, HSM_FD);
if (!fromwire_hsmd_setup_channel_reply(msg))
status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s",
tal_hex(tmpctx, msg));
}


/* ACCEPTER side of the splice. Here we handle all the accepter's steps for the
* splice. Since the channel must be in STFU mode we block the daemon here until
* the splice is finished or aborted. */
Expand Down Expand Up @@ -3586,6 +3681,10 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg)
new_inflight->last_tx = NULL;
new_inflight->i_am_initiator = false;

update_hsmd_with_splice(peer,
new_inflight,
splice_acceptor_balance(peer, TX_ACCEPTER));

update_view_from_inflights(peer);

peer->splice_state->count++;
Expand Down Expand Up @@ -3820,6 +3919,10 @@ static void splice_initiator_user_finalized(struct peer *peer)
new_inflight->last_tx = NULL;
new_inflight->i_am_initiator = true;

update_hsmd_with_splice(peer,
new_inflight,
splice_acceptor_balance(peer, TX_INITIATOR));

update_view_from_inflights(peer);

peer->splice_state->count++;
Expand Down Expand Up @@ -5191,6 +5294,7 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg)
peer_write(peer->pps, take(msg));

peer->channel_ready[LOCAL] = true;
check_mutual_channel_ready(peer);
}
else if(splicing && !peer->splice_state->locked_ready[LOCAL]) {
assert(scid);
Expand Down
43 changes: 32 additions & 11 deletions common/psbt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,33 +379,54 @@ u64 psbt_new_output_serial(struct wally_psbt *psbt, enum tx_role role)
return serial_id;
}

#include <stdio.h>
#include <common/type_to_string.h>

bool psbt_has_required_fields(struct wally_psbt *psbt)
{
psbt_set_version(psbt, 0);
fprintf(stderr, "wally_psbt: %s\n", type_to_string(tmpctx, struct wally_psbt, psbt));
psbt_set_version(psbt, 2);

u64 serial_id;
for (size_t i = 0; i < psbt->num_inputs; i++) {
const struct wally_map_item *redeem_script;
struct wally_psbt_input *input = &psbt->inputs[i];

if (!psbt_get_serial_id(&input->unknowns, &serial_id))
if (!psbt_get_serial_id(&input->unknowns, &serial_id)) {
fprintf(stderr, "in[%ld]: missing serial id\n", i);
return false;
}

/* Required because we send the full tx over the wire now */
if (!input->utxo)
/* Only insist on PSBT_IN_NON_WITNESS_UTXO (.utxo) if
* PSBT_IN_WITNESS_UTXO (.witness_utxo) is not present
* because PSBT_IN_NON_WITNESS_UTXO uses a lot of
* memory */
if (!input->witness_utxo && !input->utxo) {
fprintf(stderr, "in[%ld]: missing both utxo\n", i);
return false;
}

/* If is P2SH, redeemscript must be present */
assert(psbt->inputs[i].index < input->utxo->num_outputs);
const u8 *outscript =
wally_tx_output_get_script(tmpctx,
&input->utxo->outputs[psbt->inputs[i].index]);
redeem_script = wally_map_get_integer(&psbt->inputs[i].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04);
if (is_p2sh(outscript, NULL) && (!redeem_script || redeem_script->value_len == 0))
return false;
if (input->utxo) {
/* If is P2SH, redeemscript must be present */
assert(psbt->inputs[i].index < input->utxo->num_outputs);
const u8 *outscript =
wally_tx_output_get_script(tmpctx,
&input->utxo->outputs[psbt->inputs[i].index]);
redeem_script = wally_map_get_integer(&psbt->inputs[i].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04);
if (is_p2sh(outscript, NULL) && (!redeem_script || redeem_script->value_len == 0)) {
fprintf(stderr, "in[%ld]: missing redeemscript\n", i);
return false;
}
}
}

for (size_t i = 0; i < psbt->num_outputs; i++) {
if (!psbt_get_serial_id(&psbt->outputs[i].unknowns, &serial_id))
if (!psbt_get_serial_id(&psbt->outputs[i].unknowns, &serial_id)) {
fprintf(stderr, "out[%ld]: missing serial id\n", i);
return false;
}
}

return true;
Expand Down
Loading