Skip to content
Merged
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
50 changes: 16 additions & 34 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -3945,28 +3945,14 @@ static void check_their_witnesses(struct peer *peer,
{
struct wally_psbt *checked;
struct bitcoin_tx *tx;
bool unified;
const char *bad;
size_t j = 0;

if (chainparams->is_elements)
return;

checked = clone_psbt(tmpctx, psbt);
for (size_t i = 0; i < checked->num_inputs; i++) {
struct wally_psbt_input *in = &checked->inputs[i];
u64 in_serial;

/* Each digest then reads the spent output here, rather than
* hashing the whole prevtx again for every input. */
if (in->utxo && in->index < in->utxo->num_outputs) {
const struct wally_tx_output *out
= &in->utxo->outputs[in->index];
psbt_input_set_wit_utxo(checked, i,
tal_dup_arr(tmpctx, u8,
out->script,
out->script_len, 0),
amount_sat(out->satoshi));
}
if (i == splice_funding_index
|| !psbt_get_serial_id(&in->unknowns, &in_serial)
|| in_serial % 2 == our_role)
Expand All @@ -3981,29 +3967,25 @@ static void check_their_witnesses(struct peer *peer,
"%zu too many witness elements received",
tal_count(inws) - j);

tx = bitcoin_tx_with_psbt(tmpctx, take(checked));
/* We only know Bitcoin's digests. */
if (chainparams->is_elements)
return;
tx = psbt_tx_with_spent_outputs(tmpctx, take(checked));
if (!tx)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not build splice tx %s to check witnesses",
fmt_bitcoin_txid(tmpctx, txid));
unified = channel_type_sighash(peer->channel->type, SIGHASH_ALL)
& SIGHASH_UNIFIED;

for (size_t i = 0; i < tx->psbt->num_inputs; i++) {
u64 in_serial;
const char *bad;

if (i == splice_funding_index
|| !psbt_get_serial_id(&tx->psbt->inputs[i].unknowns,
&in_serial)
|| in_serial % 2 == our_role)
continue;
bad = psbt_input_witness_invalid(tmpctx, tx, i, unified);
if (bad)
peer_failed_err(peer->pps, &peer->channel_id,
"Bad witness for splice tx %s: %s",
fmt_bitcoin_txid(tmpctx, txid), bad);
}
bad = psbt_their_witnesses_invalid(tmpctx, tx,
our_role == TX_INITIATOR
? TX_ACCEPTER : TX_INITIATOR,
splice_funding_index,
channel_type_sighash(peer->channel->type,
SIGHASH_ALL)
& SIGHASH_UNIFIED, true);
if (bad)
peer_failed_err(peer->pps, &peer->channel_id,
"Bad witness for splice tx %s: %s",
fmt_bitcoin_txid(tmpctx, txid), bad);
}

/* Called to finish an ongoing splice OR on restart from channel_reestablish. */
Expand Down
107 changes: 100 additions & 7 deletions common/psbt_internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ static const char *check_ecdsa(const tal_t *ctx, const struct bitcoin_tx *tx,
}

/* A funding output's 2-of-2, as when the peer splices another channel in. */
static bool is_2of2_script(const u8 *script, struct pubkey *k1,
struct pubkey *k2)
{
u8 *expected;

if (tal_bytelen(script) != 71
|| !pubkey_from_der(script + 2, 33, k1)
|| !pubkey_from_der(script + 36, 33, k2))
return false;
expected = bitcoin_redeem_2of2(tmpctx, k1, k2);
return memeq(expected, tal_bytelen(expected),
script, tal_bytelen(script));
}

static const char *check_p2wsh_2of2(const tal_t *ctx,
const struct bitcoin_tx *tx,
size_t i,
Expand All @@ -256,15 +270,9 @@ static const char *check_p2wsh_2of2(const tal_t *ctx,
{
struct pubkey k1, k2;
const char *bad;
u8 *expected;

if (stack->num_items != 4 || stack->items[0].witness_len != 0
|| tal_bytelen(script) != 71
|| !pubkey_from_der(script + 2, 33, &k1)
|| !pubkey_from_der(script + 36, 33, &k2))
return tal_fmt(ctx, "P2WSH input %zu is not a 2-of-2 spend", i);
expected = bitcoin_redeem_2of2(tmpctx, &k1, &k2);
if (!memeq(expected, tal_bytelen(expected), script, tal_bytelen(script)))
|| !is_2of2_script(script, &k1, &k2))
return tal_fmt(ctx, "P2WSH input %zu is not a 2-of-2 spend", i);

/* OP_CHECKMULTISIG takes the signatures in the keys' order. */
Expand Down Expand Up @@ -423,3 +431,88 @@ const char *psbt_input_witness_invalid(const tal_t *ctx,

return tal_fmt(ctx, "input %zu spends none of P2WPKH, P2WSH or P2TR", i);
}

bool psbt_input_witness_known(const struct bitcoin_tx *tx, size_t i)
{
const struct wally_psbt *psbt = tx->psbt;
const struct wally_tx_witness_stack *stack;
const struct wally_tx_output *spent;
const struct wally_tx_witness_item *last;
struct sha256 wsh, h;
struct pubkey k1, k2;
u8 *script;

if (i >= psbt->num_inputs)
return true;
stack = psbt->inputs[i].final_witness;
spent = spent_output(psbt, i);
if (!spent || !stack || stack->num_items == 0)
return true;

if (is_p2wpkh(spent->script, spent->script_len, NULL))
return true;
last = &stack->items[stack->num_items - 1];
/* BIP341: a last item starting 0x50 is an annex, which is
* non-standard, so it's judged (and fails). */
if (is_p2tr(spent->script, spent->script_len, NULL))
return stack->num_items == 1
|| (last->witness_len && last->witness[0] == 0x50);
/* Spending any other witness program is non-standard, but for P2A
* with an empty witness, which BOLT 2 doesn't allow either. */
if (!is_p2wsh(spent->script, spent->script_len, &wsh))
return true;

script = tal_dup_arr(tmpctx, u8, last->witness, last->witness_len, 0);
sha256(&h, script, tal_bytelen(script));
return stack->num_items < 2
|| !sha256_eq(&h, &wsh)
|| is_to_remote_anchored_witness_script(script,
tal_bytelen(script))
|| is_2of2_script(script, &k1, &k2);
}

struct bitcoin_tx *psbt_tx_with_spent_outputs(const tal_t *ctx,
struct wally_psbt *psbt TAKES)
{
if (!taken(psbt))
psbt = clone_psbt(tmpctx, psbt);
for (size_t i = 0; i < psbt->num_inputs; i++) {
const struct wally_psbt_input *in = &psbt->inputs[i];
const struct wally_tx_output *out;

/* Each digest then reads the spent output here, rather than
* hashing the whole prevtx again for every input. */
if (!in->utxo || in->index >= in->utxo->num_outputs)
continue;
out = &in->utxo->outputs[in->index];
psbt_input_set_wit_utxo(psbt, i,
tal_dup_arr(tmpctx, u8, out->script,
out->script_len, 0),
amount_sat(out->satoshi));
}
return bitcoin_tx_with_psbt(ctx, take(psbt));
}

const char *psbt_their_witnesses_invalid(const tal_t *ctx,
const struct bitcoin_tx *tx,
enum tx_role their_role,
int input_index_to_ignore,
bool unified, bool strict)
{
for (size_t i = 0; i < tx->psbt->num_inputs; i++) {
u64 serial;
const char *bad;

if (i == input_index_to_ignore
|| !psbt_get_serial_id(&tx->psbt->inputs[i].unknowns,
&serial)
|| serial % 2 != their_role)
continue;
if (!strict && !psbt_input_witness_known(tx, i))
continue;
bad = psbt_input_witness_invalid(ctx, tx, i, unified);
if (bad)
return bad;
}
return NULL;
}
29 changes: 29 additions & 0 deletions common/psbt_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,33 @@ const char *psbt_input_witness_invalid(const tal_t *ctx,
const struct bitcoin_tx *tx, size_t i,
bool unified);

/* psbt_input_witness_known - Can psbt_input_witness_invalid judge it?
*
* False if input @i of @tx->psbt spends an output it doesn't evaluate: a
* P2TR output other than by a key path, or a P2WSH output with any other
* witness script. A missing or empty witness, one that doesn't match its
* P2WSH output, or a spend of any other kind of output (non-standard but
* for P2A with an empty witness) can be judged (invalid).
*/
bool psbt_input_witness_known(const struct bitcoin_tx *tx, size_t i);

/* psbt_tx_with_spent_outputs - The tx of @psbt, with each input's spent
* output attached from its prevtx, for checking witnesses. NULL if it
* can't be built. */
struct bitcoin_tx *psbt_tx_with_spent_outputs(const tal_t *ctx,
struct wally_psbt *psbt TAKES);

/* psbt_their_witnesses_invalid - Check the witnesses of a side's inputs
*
* Returns NULL if every input of @their_role in @tx (but
* @input_index_to_ignore, or -1 for none) has a final witness that
* psbt_input_witness_invalid accepts, otherwise why not. Unless @strict,
* inputs psbt_input_witness_known can't judge are skipped. Bitcoin only.
*/
const char *psbt_their_witnesses_invalid(const tal_t *ctx,
const struct bitcoin_tx *tx,
enum tx_role their_role,
int input_index_to_ignore,
bool unified, bool strict);

#endif /* LIGHTNING_COMMON_PSBT_INTERNAL_H */
Loading
Loading