mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
df-rbf: hook for rbf_init attempt, called "rbf_channel"
When we get an RBF request, we ask the/a plugin what they'd like to do about it. This pipes the request through to the plugin
This commit is contained in:
parent
50b8655cbe
commit
6474779e38
@ -55,6 +55,45 @@ static void handle_signed_psbt(struct lightningd *ld,
|
||||
take(towire_dualopend_send_tx_sigs(NULL, psbt)));
|
||||
}
|
||||
|
||||
struct rbf_channel_payload {
|
||||
struct subd *dualopend;
|
||||
struct node_id peer_id;
|
||||
|
||||
/* Info specific to this RBF */
|
||||
struct channel_id channel_id;
|
||||
struct amount_sat their_funding;
|
||||
u32 funding_feerate_per_kw;
|
||||
u32 locktime;
|
||||
|
||||
/* General info */
|
||||
u32 feerate_our_max;
|
||||
u32 feerate_our_min;
|
||||
|
||||
/* Returned from hook */
|
||||
struct amount_sat our_funding;
|
||||
struct wally_psbt *psbt;
|
||||
char *err_msg;
|
||||
};
|
||||
|
||||
static void
|
||||
rbf_channel_hook_serialize(struct rbf_channel_payload *payload,
|
||||
struct json_stream *stream)
|
||||
{
|
||||
json_object_start(stream, "rbf_channel");
|
||||
json_add_node_id(stream, "id", &payload->peer_id);
|
||||
json_add_channel_id(stream, "channel_id", &payload->channel_id);
|
||||
json_add_amount_sat_only(stream, "their_funding",
|
||||
payload->their_funding);
|
||||
json_add_num(stream, "locktime", payload->locktime);
|
||||
json_add_num(stream, "feerate_our_max",
|
||||
payload->feerate_our_max);
|
||||
json_add_num(stream, "feerate_our_min",
|
||||
payload->feerate_our_min);
|
||||
json_add_num(stream, "funding_feerate_per_kw",
|
||||
payload->funding_feerate_per_kw);
|
||||
json_object_end(stream);
|
||||
}
|
||||
|
||||
/* ~Map of the Territory~
|
||||
*
|
||||
* openchannel hook
|
||||
@ -348,6 +387,141 @@ static bool psbt_side_contribs_changed(struct wally_psbt *orig,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rbf_channel_remove_dualopend(struct subd *dualopend,
|
||||
struct rbf_channel_payload *payload)
|
||||
{
|
||||
assert(payload->dualopend == dualopend);
|
||||
payload->dualopend = NULL;
|
||||
}
|
||||
|
||||
static void rbf_channel_hook_cb(struct rbf_channel_payload *payload STEALS)
|
||||
{
|
||||
struct subd *dualopend = payload->dualopend;
|
||||
struct channel *channel;
|
||||
u8 *msg;
|
||||
|
||||
tal_steal(tmpctx, payload);
|
||||
|
||||
if (!dualopend)
|
||||
return;
|
||||
|
||||
assert(dualopend->ctype == CHANNEL);
|
||||
channel = dualopend->channel;
|
||||
|
||||
tal_del_destructor2(dualopend, rbf_channel_remove_dualopend, payload);
|
||||
|
||||
if (channel->state != DUALOPEND_AWAITING_LOCKIN) {
|
||||
log_debug(channel->log,
|
||||
"rbf_channel hook returned, but channel in state"
|
||||
" %s", channel_state_name(channel));
|
||||
msg = towire_dualopend_fail(NULL, "Peer error. Channel"
|
||||
" not ready for RBF attempt.");
|
||||
return subd_send_msg(dualopend, take(msg));
|
||||
}
|
||||
|
||||
if (payload->err_msg) {
|
||||
log_debug(channel->log,
|
||||
"rbf_channel hook rejects and says '%s'",
|
||||
payload->err_msg);
|
||||
msg = towire_dualopend_fail(NULL, payload->err_msg);
|
||||
return subd_send_msg(dualopend, take(msg));
|
||||
}
|
||||
|
||||
/* Update channel info for "inflight" attempt.
|
||||
* Note that an 'inflight' doesn't get created
|
||||
* until *after* we've completed the tx_sigs exchange */
|
||||
// FIXME: reinstitute the uc?? but where??
|
||||
//struct uncommitted_channel *uc;
|
||||
msg = towire_dualopend_got_rbf_offer_reply(NULL,
|
||||
payload->our_funding,
|
||||
payload->psbt);
|
||||
subd_send_msg(dualopend, take(msg));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool
|
||||
rbf_channel_hook_deserialize(struct rbf_channel_payload *payload,
|
||||
const char *buffer,
|
||||
const jsmntok_t *toks)
|
||||
{
|
||||
struct subd *dualopend = payload->dualopend;
|
||||
struct channel *channel;
|
||||
|
||||
if (!dualopend) {
|
||||
tal_free(dualopend);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(dualopend->ctype == CHANNEL);
|
||||
channel = dualopend->channel;
|
||||
|
||||
/* FIXME: move to new json extraction */
|
||||
const jsmntok_t *t_result = json_get_member(buffer, toks, "result");
|
||||
if (!t_result)
|
||||
fatal("Plugin returned an invalid response to the"
|
||||
" rbf_channel hook: %.*s",
|
||||
json_tok_full_len(toks),
|
||||
json_tok_full(buffer, toks));
|
||||
|
||||
if (json_tok_streq(buffer, t_result, "reject")) {
|
||||
if (json_get_member(buffer, toks, "psbt"))
|
||||
fatal("Plugin rejected rbf_channel but"
|
||||
" also set `psbt`");
|
||||
if (json_get_member(buffer, toks, "our_funding_msat"))
|
||||
fatal("Plugin rejected rbf_channel but"
|
||||
" also set `our_funding_msat`");
|
||||
|
||||
const jsmntok_t *t_errmsg = json_get_member(buffer, toks,
|
||||
"error_message");
|
||||
if (t_errmsg)
|
||||
payload->err_msg = json_strdup(payload, buffer,
|
||||
t_errmsg);
|
||||
else
|
||||
payload->err_msg = "";
|
||||
|
||||
rbf_channel_hook_cb(payload);
|
||||
return false;
|
||||
} else if (!json_tok_streq(buffer, t_result, "continue"))
|
||||
fatal("Plugin returned invalid response to rbf_channel hook:"
|
||||
" %.*s", json_tok_full_len(toks),
|
||||
json_tok_full(buffer, toks));
|
||||
|
||||
if (!hook_extract_psbt(payload, dualopend, buffer, toks,
|
||||
"rbf_channel", true, &payload->psbt))
|
||||
return false;
|
||||
|
||||
if (payload->psbt) {
|
||||
enum tx_role our_role = channel->opener == LOCAL ?
|
||||
TX_INITIATOR : TX_ACCEPTER;
|
||||
psbt_add_serials(payload->psbt, our_role);
|
||||
}
|
||||
|
||||
if (payload->psbt && !psbt_has_required_fields(payload->psbt))
|
||||
fatal("Plugin supplied PSBT that's missing"
|
||||
" required fields: %s",
|
||||
type_to_string(tmpctx, struct wally_psbt,
|
||||
payload->psbt));
|
||||
if (!hook_extract_amount(dualopend, buffer, toks,
|
||||
"our_funding_msat", &payload->our_funding))
|
||||
fatal("Plugin failed to supply our_funding_msat field");
|
||||
|
||||
if (!payload->psbt &&
|
||||
!amount_sat_eq(payload->our_funding, AMOUNT_SAT(0))) {
|
||||
|
||||
log_broken(channel->log, "`our_funding_msat` returned"
|
||||
" but no `psbt` present. %.*s",
|
||||
json_tok_full_len(toks),
|
||||
json_tok_full(buffer, toks));
|
||||
|
||||
payload->err_msg = "Client error. Unable to continue";
|
||||
rbf_channel_hook_cb(payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* dualopend dies? Remove dualopend ptr from payload */
|
||||
static void openchannel2_remove_dualopend(struct subd *dualopend,
|
||||
struct openchannel2_payload *payload)
|
||||
@ -671,6 +845,12 @@ REGISTER_PLUGIN_HOOK(openchannel2_sign,
|
||||
openchannel2_sign_hook_serialize,
|
||||
struct openchannel2_psbt_payload *);
|
||||
|
||||
REGISTER_PLUGIN_HOOK(rbf_channel,
|
||||
rbf_channel_hook_deserialize,
|
||||
rbf_channel_hook_cb,
|
||||
rbf_channel_hook_serialize,
|
||||
struct rbf_channel_payload *);
|
||||
|
||||
/* Steals fields from uncommitted_channel: returns NULL if can't generate a
|
||||
* key for this channel (shouldn't happen!). */
|
||||
static struct channel *
|
||||
@ -1466,6 +1646,46 @@ static void accepter_psbt_changed(struct subd *dualopend,
|
||||
plugin_hook_call_openchannel2_changed(dualopend->ld, payload);
|
||||
}
|
||||
|
||||
static void rbf_got_offer(struct subd *dualopend, const u8 *msg)
|
||||
{
|
||||
/* We expect the channel to still exist?! */
|
||||
struct channel *channel;
|
||||
struct rbf_channel_payload *payload;
|
||||
|
||||
assert(dualopend->ctype == CHANNEL);
|
||||
channel = dualopend->channel;
|
||||
|
||||
payload = tal(dualopend, struct rbf_channel_payload);
|
||||
payload->dualopend = dualopend;
|
||||
|
||||
if (!fromwire_dualopend_got_rbf_offer(msg,
|
||||
&payload->channel_id,
|
||||
&payload->their_funding,
|
||||
&payload->funding_feerate_per_kw,
|
||||
&payload->locktime)) {
|
||||
log_broken(channel->log, "Malformed dualopend_got_rbf_offer %s",
|
||||
tal_hex(msg, msg));
|
||||
// FIXME: is this the right thing to do here?
|
||||
tal_free(dualopend);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fill in general channel info from channel */
|
||||
payload->peer_id = channel->peer->id;
|
||||
payload->feerate_our_max = feerate_max(dualopend->ld, NULL);
|
||||
payload->feerate_our_min = feerate_min(dualopend->ld, NULL);
|
||||
|
||||
/* Set our contributions to empty, in case there is no plugin */
|
||||
payload->our_funding = AMOUNT_SAT(0);
|
||||
payload->psbt = NULL;
|
||||
|
||||
/* No error message known (yet) */
|
||||
payload->err_msg = NULL;
|
||||
|
||||
tal_add_destructor2(dualopend, rbf_channel_remove_dualopend, payload);
|
||||
plugin_hook_call_rbf_channel(dualopend->ld, payload);
|
||||
}
|
||||
|
||||
static void accepter_got_offer(struct subd *dualopend,
|
||||
struct uncommitted_channel *uc,
|
||||
const u8 *msg)
|
||||
@ -2002,7 +2222,7 @@ static unsigned int dual_opend_msg(struct subd *dualopend,
|
||||
accepter_got_offer(dualopend, uc, msg);
|
||||
return 0;
|
||||
case WIRE_DUALOPEND_GOT_RBF_OFFER:
|
||||
// FIXME: do this
|
||||
rbf_got_offer(dualopend, msg);
|
||||
return 0;
|
||||
case WIRE_DUALOPEND_PSBT_CHANGED:
|
||||
if (uc->fc) {
|
||||
|
@ -102,13 +102,13 @@ msgdata,dualopend_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len
|
||||
# dualopend->master: they offered a RBF, should we continue?
|
||||
msgtype,dualopend_got_rbf_offer,7500
|
||||
msgdata,dualopend_got_rbf_offer,channel_id,channel_id,
|
||||
msgdata,dualopend_got_rbf_offer,opener_funding,amount_sat,
|
||||
msgdata,dualopend_got_rbf_offer,their_funding,amount_sat,
|
||||
msgdata,dualopend_got_rbf_offer,funding_feerate_per_kw,u32,
|
||||
msgdata,dualopend_got_rbf_offer,locktime,u32,
|
||||
|
||||
# master->dualopend: reply back with our funding info/contribs
|
||||
msgtype,dualopend_got_rbf_offer_reply,7505
|
||||
msgdata,dualopend_got_rbf_offer_reply,accepter_funding,amount_sat,
|
||||
msgdata,dualopend_got_rbf_offer_reply,our_funding,amount_sat,
|
||||
msgdata,dualopend_got_rbf_offer_reply,psbt,wally_psbt,
|
||||
|
||||
# dualopend->master: is this a valid RBF candidate transaction?
|
||||
|
Can't render this file because it has a wrong number of fields in line 13.
|
18
openingd/dualopend_wiregen.c
generated
18
openingd/dualopend_wiregen.c
generated
@ -365,19 +365,19 @@ bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct
|
||||
|
||||
/* WIRE: DUALOPEND_GOT_RBF_OFFER */
|
||||
/* dualopend->master: they offered a RBF */
|
||||
u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, u32 funding_feerate_per_kw, u32 locktime)
|
||||
u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat their_funding, u32 funding_feerate_per_kw, u32 locktime)
|
||||
{
|
||||
u8 *p = tal_arr(ctx, u8, 0);
|
||||
|
||||
towire_u16(&p, WIRE_DUALOPEND_GOT_RBF_OFFER);
|
||||
towire_channel_id(&p, channel_id);
|
||||
towire_amount_sat(&p, opener_funding);
|
||||
towire_amount_sat(&p, their_funding);
|
||||
towire_u32(&p, funding_feerate_per_kw);
|
||||
towire_u32(&p, locktime);
|
||||
|
||||
return memcheck(p, tal_count(p));
|
||||
}
|
||||
bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, u32 *funding_feerate_per_kw, u32 *locktime)
|
||||
bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *their_funding, u32 *funding_feerate_per_kw, u32 *locktime)
|
||||
{
|
||||
const u8 *cursor = p;
|
||||
size_t plen = tal_count(p);
|
||||
@ -385,7 +385,7 @@ bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_
|
||||
if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_GOT_RBF_OFFER)
|
||||
return false;
|
||||
fromwire_channel_id(&cursor, &plen, channel_id);
|
||||
*opener_funding = fromwire_amount_sat(&cursor, &plen);
|
||||
*their_funding = fromwire_amount_sat(&cursor, &plen);
|
||||
*funding_feerate_per_kw = fromwire_u32(&cursor, &plen);
|
||||
*locktime = fromwire_u32(&cursor, &plen);
|
||||
return cursor != NULL;
|
||||
@ -393,24 +393,24 @@ bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_
|
||||
|
||||
/* WIRE: DUALOPEND_GOT_RBF_OFFER_REPLY */
|
||||
/* master->dualopend: reply back with our funding info/contribs */
|
||||
u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt)
|
||||
u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat our_funding, const struct wally_psbt *psbt)
|
||||
{
|
||||
u8 *p = tal_arr(ctx, u8, 0);
|
||||
|
||||
towire_u16(&p, WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY);
|
||||
towire_amount_sat(&p, accepter_funding);
|
||||
towire_amount_sat(&p, our_funding);
|
||||
towire_wally_psbt(&p, psbt);
|
||||
|
||||
return memcheck(p, tal_count(p));
|
||||
}
|
||||
bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt)
|
||||
bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *our_funding, struct wally_psbt **psbt)
|
||||
{
|
||||
const u8 *cursor = p;
|
||||
size_t plen = tal_count(p);
|
||||
|
||||
if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY)
|
||||
return false;
|
||||
*accepter_funding = fromwire_amount_sat(&cursor, &plen);
|
||||
*our_funding = fromwire_amount_sat(&cursor, &plen);
|
||||
*psbt = fromwire_wally_psbt(ctx, &cursor, &plen);
|
||||
return cursor != NULL;
|
||||
}
|
||||
@ -966,4 +966,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak)
|
||||
*leak = fromwire_bool(&cursor, &plen);
|
||||
return cursor != NULL;
|
||||
}
|
||||
// SHA256STAMP:e514a16cf96dfcaf44217cf344ce75e2b8cbcb460901c610d9b75e22937636cd
|
||||
// SHA256STAMP:0f0daed93a4de2552ca122b969c4ac215ab89e3d5babc727b963fcf02f85980d
|
||||
|
10
openingd/dualopend_wiregen.h
generated
10
openingd/dualopend_wiregen.h
generated
@ -108,13 +108,13 @@ bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct
|
||||
|
||||
/* WIRE: DUALOPEND_GOT_RBF_OFFER */
|
||||
/* dualopend->master: they offered a RBF */
|
||||
u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, u32 funding_feerate_per_kw, u32 locktime);
|
||||
bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, u32 *funding_feerate_per_kw, u32 *locktime);
|
||||
u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat their_funding, u32 funding_feerate_per_kw, u32 locktime);
|
||||
bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *their_funding, u32 *funding_feerate_per_kw, u32 *locktime);
|
||||
|
||||
/* WIRE: DUALOPEND_GOT_RBF_OFFER_REPLY */
|
||||
/* master->dualopend: reply back with our funding info/contribs */
|
||||
u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt);
|
||||
bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt);
|
||||
u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat our_funding, const struct wally_psbt *psbt);
|
||||
bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *our_funding, struct wally_psbt **psbt);
|
||||
|
||||
/* WIRE: DUALOPEND_RBF_VALIDATE */
|
||||
/* dualopend->master: is this a valid RBF candidate transaction? */
|
||||
@ -223,4 +223,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak);
|
||||
|
||||
|
||||
#endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */
|
||||
// SHA256STAMP:e514a16cf96dfcaf44217cf344ce75e2b8cbcb460901c610d9b75e22937636cd
|
||||
// SHA256STAMP:0f0daed93a4de2552ca122b969c4ac215ab89e3d5babc727b963fcf02f85980d
|
||||
|
Loading…
Reference in New Issue
Block a user