df-rbf: validate that the candidate funding transaction is valid

We need to make sure that there's at least one input that's represented
in every single RBF-attempt for this channel, to prevent "parallel"
subsequent RBFs from succeeding/opening (the multi-channel backdoor?!)
This commit is contained in:
lisa neigut 2021-01-12 20:40:37 -06:00 committed by Rusty Russell
parent b30489310b
commit 0d8351155e
5 changed files with 155 additions and 2 deletions

View File

@ -4,7 +4,9 @@
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/ccan/mem/mem.h>
#include <ccan/ccan/take/take.h>
#include <ccan/ccan/tal/tal.h>
#include <ccan/short_types/short_types.h>
#include <common/amount.h>
#include <common/channel_config.h>
@ -1603,6 +1605,70 @@ static void handle_peer_tx_sigs_msg(struct subd *dualopend,
channel->psbt);
}
static void handle_validate_rbf(struct subd *dualopend,
const u8 *msg)
{
struct wally_psbt *candidate_psbt;
struct channel_inflight *inflight;
struct channel *channel = dualopend->channel;
bool *inputs_present;
if (!fromwire_dualopend_rbf_validate(tmpctx, msg,
&candidate_psbt)) {
channel_internal_error(channel,
"Malformed dualopend_rbf_validate %s",
tal_hex(tmpctx, msg));
return;
}
inputs_present = tal_arr(tmpctx, bool, candidate_psbt->num_inputs);
memset(inputs_present, true, tal_bytelen(inputs_present));
/* Check all previous funding transactions */
list_for_each(&channel->inflights, inflight, list) {
/* Remove every non-matching input from set */
for (size_t i = 0; i < candidate_psbt->num_inputs; i++) {
struct wally_tx_input *input =
&candidate_psbt->tx->inputs[i];
struct bitcoin_txid in_txid;
wally_tx_input_get_txid(input, &in_txid);
if (!psbt_has_input(inflight->funding_psbt,
&in_txid, input->index))
inputs_present[i] = false;
}
}
/* Are there any inputs that were present on all txs? */
if (memeqzero(inputs_present, tal_bytelen(inputs_present))) {
char *errmsg;
inflight = list_tail(&channel->inflights,
struct channel_inflight,
list);
assert(inflight);
errmsg = tal_fmt(tmpctx, "No overlapping input"
" present. New: %s, last: %s",
type_to_string(tmpctx,
struct wally_psbt,
candidate_psbt),
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
msg = towire_dualopend_fail(NULL, errmsg);
goto send_msg;
}
/* FIXME: check that total fee paid is greater than last */
msg = towire_dualopend_rbf_valid(NULL);
send_msg:
subd_send_msg(channel->owner, take(msg));
}
static struct command_result *
json_openchannel_signed(struct command *cmd,
@ -1962,6 +2028,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend,
accepter_commit_received(dualopend,
uc, fds, msg);
return 0;
case WIRE_DUALOPEND_RBF_VALIDATE:
handle_validate_rbf(dualopend, msg);
return 0;
case WIRE_DUALOPEND_FUNDING_SIGS:
handle_peer_tx_sigs_msg(dualopend, msg);
return 0;
@ -2002,6 +2071,7 @@ static unsigned int dual_opend_msg(struct subd *dualopend,
case WIRE_DUALOPEND_OPENER_INIT:
case WIRE_DUALOPEND_GOT_OFFER_REPLY:
case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY:
case WIRE_DUALOPEND_RBF_VALID:
case WIRE_DUALOPEND_FAIL:
case WIRE_DUALOPEND_PSBT_UPDATED:
case WIRE_DUALOPEND_SEND_TX_SIGS:

View File

@ -2592,6 +2592,20 @@ static void rbf_start(struct state *state, const u8 *rbf_msg)
&tx_state->psbt, state->our_role))
return;
/* Is this an eligible RBF (at least one overlapping input) */
msg = towire_dualopend_rbf_validate(NULL, tx_state->psbt);
wire_sync_write(REQ_FD, take(msg));
msg = wire_sync_read(tmpctx, REQ_FD);
if ((msg_type = fromwire_peektype(msg)) == WIRE_DUALOPEND_FAIL) {
if (!fromwire_dualopend_fail(msg, msg, &err_reason))
master_badmsg(msg_type, msg);
rbf_failed(state, "%s", err_reason);
}
if (!fromwire_dualopend_rbf_valid(msg))
master_badmsg(WIRE_DUALOPEND_RBF_VALID, msg);
/* Find the funding transaction txid */
psbt_txid(NULL, tx_state->psbt, &tx_state->funding_txid, NULL);
@ -2962,10 +2976,12 @@ static u8 *handle_master_in(struct state *state)
case WIRE_DUALOPEND_PSBT_UPDATED:
case WIRE_DUALOPEND_GOT_OFFER_REPLY:
case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY:
case WIRE_DUALOPEND_RBF_VALID:
/* Messages we send */
case WIRE_DUALOPEND_GOT_OFFER:
case WIRE_DUALOPEND_GOT_RBF_OFFER:
case WIRE_DUALOPEND_RBF_VALIDATE:
case WIRE_DUALOPEND_PSBT_CHANGED:
case WIRE_DUALOPEND_COMMIT_RCVD:
case WIRE_DUALOPEND_FUNDING_SIGS:

View File

@ -111,6 +111,13 @@ msgtype,dualopend_got_rbf_offer_reply,7505
msgdata,dualopend_got_rbf_offer_reply,accepter_funding,amount_sat,
msgdata,dualopend_got_rbf_offer_reply,psbt,wally_psbt,
# dualopend->master: is this a valid RBF candidate transaction?
msgtype,dualopend_rbf_validate,7506
msgdata,dualopend_rbf_validate,proposed_funding_psbt,wally_psbt,
# master->dualopend: this is a valid RBF candidate transaction
msgtype,dualopend_rbf_valid,7507
# dualopend->master: ready to commit channel open to database and
# get some signatures for the funding_tx.
msgtype,dualopend_commit_rcvd,7007

Can't render this file because it has a wrong number of fields in line 13.

View File

@ -26,6 +26,8 @@ const char *dualopend_wire_name(int e)
case WIRE_DUALOPEND_GOT_OFFER_REPLY: return "WIRE_DUALOPEND_GOT_OFFER_REPLY";
case WIRE_DUALOPEND_GOT_RBF_OFFER: return "WIRE_DUALOPEND_GOT_RBF_OFFER";
case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: return "WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY";
case WIRE_DUALOPEND_RBF_VALIDATE: return "WIRE_DUALOPEND_RBF_VALIDATE";
case WIRE_DUALOPEND_RBF_VALID: return "WIRE_DUALOPEND_RBF_VALID";
case WIRE_DUALOPEND_COMMIT_RCVD: return "WIRE_DUALOPEND_COMMIT_RCVD";
case WIRE_DUALOPEND_PSBT_CHANGED: return "WIRE_DUALOPEND_PSBT_CHANGED";
case WIRE_DUALOPEND_PSBT_UPDATED: return "WIRE_DUALOPEND_PSBT_UPDATED";
@ -60,6 +62,8 @@ bool dualopend_wire_is_defined(u16 type)
case WIRE_DUALOPEND_GOT_OFFER_REPLY:;
case WIRE_DUALOPEND_GOT_RBF_OFFER:;
case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY:;
case WIRE_DUALOPEND_RBF_VALIDATE:;
case WIRE_DUALOPEND_RBF_VALID:;
case WIRE_DUALOPEND_COMMIT_RCVD:;
case WIRE_DUALOPEND_PSBT_CHANGED:;
case WIRE_DUALOPEND_PSBT_UPDATED:;
@ -411,6 +415,48 @@ bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, str
return cursor != NULL;
}
/* WIRE: DUALOPEND_RBF_VALIDATE */
/* dualopend->master: is this a valid RBF candidate transaction? */
u8 *towire_dualopend_rbf_validate(const tal_t *ctx, const struct wally_psbt *proposed_funding_psbt)
{
u8 *p = tal_arr(ctx, u8, 0);
towire_u16(&p, WIRE_DUALOPEND_RBF_VALIDATE);
towire_wally_psbt(&p, proposed_funding_psbt);
return memcheck(p, tal_count(p));
}
bool fromwire_dualopend_rbf_validate(const tal_t *ctx, const void *p, struct wally_psbt **proposed_funding_psbt)
{
const u8 *cursor = p;
size_t plen = tal_count(p);
if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_RBF_VALIDATE)
return false;
*proposed_funding_psbt = fromwire_wally_psbt(ctx, &cursor, &plen);
return cursor != NULL;
}
/* WIRE: DUALOPEND_RBF_VALID */
/* master->dualopend: this is a valid RBF candidate transaction */
u8 *towire_dualopend_rbf_valid(const tal_t *ctx)
{
u8 *p = tal_arr(ctx, u8, 0);
towire_u16(&p, WIRE_DUALOPEND_RBF_VALID);
return memcheck(p, tal_count(p));
}
bool fromwire_dualopend_rbf_valid(const void *p)
{
const u8 *cursor = p;
size_t plen = tal_count(p);
if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_RBF_VALID)
return false;
return cursor != NULL;
}
/* WIRE: DUALOPEND_COMMIT_RCVD */
/* dualopend->master: ready to commit channel open to database and */
/* get some signatures for the funding_tx. */
@ -920,4 +966,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak)
*leak = fromwire_bool(&cursor, &plen);
return cursor != NULL;
}
// SHA256STAMP:65a94b9b35802a6cd6c68f724e83ce4866adb3122a392d6741ec7c791ef1fd6c
// SHA256STAMP:e514a16cf96dfcaf44217cf344ce75e2b8cbcb460901c610d9b75e22937636cd

View File

@ -31,6 +31,10 @@ enum dualopend_wire {
WIRE_DUALOPEND_GOT_RBF_OFFER = 7500,
/* master->dualopend: reply back with our funding info/contribs */
WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY = 7505,
/* dualopend->master: is this a valid RBF candidate transaction? */
WIRE_DUALOPEND_RBF_VALIDATE = 7506,
/* master->dualopend: this is a valid RBF candidate transaction */
WIRE_DUALOPEND_RBF_VALID = 7507,
/* dualopend->master: ready to commit channel open to database and */
/* get some signatures for the funding_tx. */
WIRE_DUALOPEND_COMMIT_RCVD = 7007,
@ -112,6 +116,16 @@ bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_
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);
/* WIRE: DUALOPEND_RBF_VALIDATE */
/* dualopend->master: is this a valid RBF candidate transaction? */
u8 *towire_dualopend_rbf_validate(const tal_t *ctx, const struct wally_psbt *proposed_funding_psbt);
bool fromwire_dualopend_rbf_validate(const tal_t *ctx, const void *p, struct wally_psbt **proposed_funding_psbt);
/* WIRE: DUALOPEND_RBF_VALID */
/* master->dualopend: this is a valid RBF candidate transaction */
u8 *towire_dualopend_rbf_valid(const tal_t *ctx);
bool fromwire_dualopend_rbf_valid(const void *p);
/* WIRE: DUALOPEND_COMMIT_RCVD */
/* dualopend->master: ready to commit channel open to database and */
/* get some signatures for the funding_tx. */
@ -209,4 +223,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak);
#endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */
// SHA256STAMP:65a94b9b35802a6cd6c68f724e83ce4866adb3122a392d6741ec7c791ef1fd6c
// SHA256STAMP:e514a16cf96dfcaf44217cf344ce75e2b8cbcb460901c610d9b75e22937636cd