hsmd: implement the hsmd outpoint check

Tihis commit is implementing a 2-phase commit between
the signer the node and the peer.

The main reason for this is that everybody must agree on the lock,
otherwise one of them will want N signatures (on the splice candidates),
and another will produce only 1 signature.

check_outpoint is the "prepare" for the signer, and lock_outpoint is the
"commit". if check_outpoint returns true, lock_outpoint must not fail.

Link: https://github.com/ElementsProject/lightning/issues/6722
Suggested-by: @devrandom
Co-Developed-by: Ken Sedgwick <ken@bonsai.com>
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
Ken Sedgwick 2023-09-24 15:12:02 -07:00 committed by Rusty Russell
parent 485cabb25e
commit 44798e298c
4 changed files with 99 additions and 3 deletions

View file

@ -663,6 +663,48 @@ 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;
/* FIXME(vincenzopalazzo): Sleeping in a deamon of cln should be never fine
* howerver the core deamon of cln will never trigger the sleep.
*
* I think that the correct solution for this is a timer base solution, but this
* required a little bit of refactoring */
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. */
@ -733,6 +775,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);
@ -808,6 +853,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",

View file

@ -1359,7 +1359,8 @@ bool peer_start_channeld(struct channel *channel,
| HSM_PERM_SIGN_REMOTE_TX
| HSM_PERM_SIGN_ONCHAIN_TX
| HSM_PERM_SIGN_CLOSING_TX
| HSM_PERM_SIGN_SPLICE_TX);
| HSM_PERM_SIGN_SPLICE_TX
| HSM_PERM_LOCK_OUTPOINT);
channel_set_owner(channel,
new_channel_subd(channel, ld,

View file

@ -3809,7 +3809,8 @@ bool peer_start_dualopend(struct peer *peer,
hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->unsaved_dbid,
HSM_PERM_COMMITMENT_POINT
| HSM_PERM_SIGN_REMOTE_TX
| HSM_PERM_SIGN_WILL_FUND_OFFER);
| HSM_PERM_SIGN_WILL_FUND_OFFER
| HSM_PERM_LOCK_OUTPOINT);
channel->owner = new_channel_subd(channel,
peer->ld,
@ -3881,7 +3882,8 @@ bool peer_restart_dualopend(struct peer *peer,
hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid,
HSM_PERM_COMMITMENT_POINT
| HSM_PERM_SIGN_REMOTE_TX
| HSM_PERM_SIGN_WILL_FUND_OFFER);
| HSM_PERM_SIGN_WILL_FUND_OFFER
| HSM_PERM_LOCK_OUTPOINT);
channel_set_owner(channel,
new_channel_subd(channel, peer->ld,

View file

@ -426,6 +426,51 @@ static void billboard_update(struct state *state)
peer_billboard(false, update);
}
static void lock_signer_outpoint(const struct bitcoin_outpoint *outpoint)
{
const u8 *msg;
bool is_buried = false;
/* FIXME(vincenzopalazzo): Sleeping in a deamon of cln should be never fine
* howerver the core deamon of cln will never trigger the sleep.
*
* I think that the correct solution for this is a timer base solution, but this
* required to implement all the timers in the deamon. */
do {
/* Make sure the hsmd agrees that this outpoint is
* sufficiently buried. */
msg = towire_hsmd_check_outpoint(NULL, &outpoint->txid, outpoint->n);
wire_sync_write(HSM_FD, take(msg));
msg = wire_sync_read(tmpctx, HSM_FD);
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);
wire_sync_write(HSM_FD, take(msg));
msg = wire_sync_read(tmpctx, HSM_FD);
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 state *state)
{
if (state->channel_ready[LOCAL] && state->channel_ready[REMOTE])
lock_signer_outpoint(&state->channel->funding);
}
static void send_shutdown(struct state *state, const u8 *final_scriptpubkey)
{
u8 *msg;
@ -1265,6 +1310,7 @@ static u8 *handle_channel_ready(struct state *state, u8 *msg)
}
state->channel_ready[REMOTE] = true;
check_mutual_channel_ready(state);
billboard_update(state);
if (state->channel_ready[LOCAL])
@ -3789,6 +3835,7 @@ static void send_channel_ready(struct state *state)
peer_write(state->pps, take(msg));
state->channel_ready[LOCAL] = true;
check_mutual_channel_ready(state);
billboard_update(state);
}