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:
niftynei 2021-01-13 19:54:14 -06:00 committed by neil saitug
parent 50b8655cbe
commit 6474779e38
4 changed files with 237 additions and 17 deletions

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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