mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-21 14:24:09 +01:00
onchaind: use a point-of-last-resort if we see an unknown transaction.
This may have been supplied by the peer if it's nice and supports option_data_loss_protect. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
6aed936799
commit
1a4084442b
6 changed files with 120 additions and 12 deletions
|
@ -467,7 +467,8 @@ enum watch_result onchaind_funding_spent(struct channel *channel,
|
|||
channel->last_htlc_sigs,
|
||||
tal_count(stubs),
|
||||
channel->min_possible_feerate,
|
||||
channel->max_possible_feerate);
|
||||
channel->max_possible_feerate,
|
||||
channel->future_per_commitment_point);
|
||||
subd_send_msg(channel->owner, take(msg));
|
||||
|
||||
/* FIXME: Don't queue all at once, use an empty cb... */
|
||||
|
|
|
@ -2117,6 +2117,94 @@ static void handle_their_unilateral(const struct bitcoin_tx *tx,
|
|||
wait_for_resolved(outs);
|
||||
}
|
||||
|
||||
static void handle_unknown_commitment(const struct bitcoin_tx *tx,
|
||||
u32 tx_blockheight,
|
||||
u64 commit_num,
|
||||
const struct bitcoin_txid *txid,
|
||||
const struct pubkey *possible_remote_per_commitment_point,
|
||||
const struct basepoints basepoints[NUM_SIDES],
|
||||
const struct htlc_stub *htlcs,
|
||||
const bool *tell_if_missing,
|
||||
struct tracked_output **outs)
|
||||
{
|
||||
struct keyset *ks;
|
||||
int to_us_output = -1;
|
||||
u8 *local_script;
|
||||
|
||||
resolved_by_other(outs[0], txid, UNKNOWN_UNILATERAL);
|
||||
|
||||
if (!possible_remote_per_commitment_point)
|
||||
goto search_done;
|
||||
|
||||
keyset = ks = tal(tx, struct keyset);
|
||||
if (!derive_keyset(possible_remote_per_commitment_point,
|
||||
&basepoints[REMOTE],
|
||||
&basepoints[LOCAL],
|
||||
ks))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Deriving keyset for possible_remote_per_commitment_point %s",
|
||||
type_to_string(tmpctx, struct pubkey,
|
||||
possible_remote_per_commitment_point));
|
||||
|
||||
local_script = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key);
|
||||
for (size_t i = 0; i < tal_count(tx->output); i++) {
|
||||
struct tracked_output *out;
|
||||
|
||||
if (local_script
|
||||
&& scripteq(tx->output[i].script, local_script)) {
|
||||
/* BOLT #5:
|
||||
*
|
||||
* - MAY take no action in regard to the associated
|
||||
* `to_remote`, which is simply a P2WPKH output to
|
||||
* the *local node*.
|
||||
* - Note: `to_remote` is considered *resolved* by the
|
||||
* commitment transaction itself.
|
||||
*/
|
||||
out = new_tracked_output(&outs, txid, tx_blockheight,
|
||||
UNKNOWN_UNILATERAL,
|
||||
i, tx->output[i].amount,
|
||||
OUTPUT_TO_US, NULL, NULL, NULL);
|
||||
ignore_output(out);
|
||||
local_script = NULL;
|
||||
|
||||
/* Tell the master that it will want to add
|
||||
* this UTXO to its outputs */
|
||||
wire_sync_write(REQ_FD, towire_onchain_add_utxo(
|
||||
tmpctx, txid, i,
|
||||
possible_remote_per_commitment_point,
|
||||
tx->output[i].amount,
|
||||
tx_blockheight));
|
||||
to_us_output = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
search_done:
|
||||
if (to_us_output == -1) {
|
||||
status_broken("FUNDS LOST. Unknown commitment #%"PRIu64"!",
|
||||
commit_num);
|
||||
init_reply("ERROR: FUNDS LOST. Unknown commitment!");
|
||||
} else {
|
||||
status_broken("ERROR: Unknown commitment #%"PRIu64
|
||||
", recovering our funds!",
|
||||
commit_num);
|
||||
init_reply("ERROR: Unknown commitment, recovering our funds!");
|
||||
}
|
||||
|
||||
/* Tell master to give up on HTLCs immediately. */
|
||||
for (size_t i = 0; i < tal_count(htlcs); i++) {
|
||||
u8 *msg;
|
||||
|
||||
if (!tell_if_missing[i])
|
||||
continue;
|
||||
|
||||
msg = towire_onchain_missing_htlc_output(NULL, &htlcs[i]);
|
||||
wire_sync_write(REQ_FD, take(msg));
|
||||
}
|
||||
|
||||
wait_for_resolved(outs);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
setup_locale();
|
||||
|
@ -2136,6 +2224,7 @@ int main(int argc, char *argv[])
|
|||
struct htlc_stub *htlcs;
|
||||
bool *tell_if_missing, *tell_immediately;
|
||||
u32 tx_blockheight;
|
||||
struct pubkey *possible_remote_per_commitment_point;
|
||||
|
||||
subdaemon_setup(argc, argv);
|
||||
|
||||
|
@ -2166,7 +2255,8 @@ int main(int argc, char *argv[])
|
|||
&remote_htlc_sigs,
|
||||
&num_htlcs,
|
||||
&min_possible_feerate,
|
||||
&max_possible_feerate)) {
|
||||
&max_possible_feerate,
|
||||
&possible_remote_per_commitment_point)) {
|
||||
master_badmsg(WIRE_ONCHAIN_INIT, msg);
|
||||
}
|
||||
|
||||
|
@ -2284,13 +2374,16 @@ int main(int argc, char *argv[])
|
|||
tell_if_missing,
|
||||
tell_immediately,
|
||||
outs);
|
||||
} else
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Unknown commitment index %"PRIu64
|
||||
" for tx %s",
|
||||
commit_num,
|
||||
type_to_string(tmpctx, struct bitcoin_tx,
|
||||
tx));
|
||||
} else {
|
||||
handle_unknown_commitment(tx, tx_blockheight,
|
||||
commit_num,
|
||||
&txid,
|
||||
possible_remote_per_commitment_point,
|
||||
basepoints,
|
||||
htlcs,
|
||||
tell_if_missing,
|
||||
outs);
|
||||
}
|
||||
}
|
||||
|
||||
/* We're done! */
|
||||
|
|
|
@ -13,6 +13,9 @@ enum tx_type {
|
|||
/* Their unilateral: spends funding */
|
||||
THEIR_UNILATERAL,
|
||||
|
||||
/* Unknown unilateral (presumably theirs): spends funding */
|
||||
UNKNOWN_UNILATERAL,
|
||||
|
||||
/* Our unilateral: spends funding */
|
||||
OUR_UNILATERAL,
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ onchain_init,,htlc_signature,num_htlc_sigs*secp256k1_ecdsa_signature
|
|||
onchain_init,,num_htlcs,u64
|
||||
onchain_init,,min_possible_feerate,u32
|
||||
onchain_init,,max_possible_feerate,u32
|
||||
onchain_init,,possible_remote_per_commit_point,?struct pubkey
|
||||
|
||||
#include <onchaind/onchain_wire.h>
|
||||
# This is all the HTLCs: one per message
|
||||
|
|
|
|
@ -37,7 +37,7 @@ bool fromwire_onchain_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UN
|
|||
bool fromwire_onchain_htlc(const void *p UNNEEDED, struct htlc_stub *htlc UNNEEDED, bool *tell_if_missing UNNEEDED, bool *tell_immediately UNNEEDED)
|
||||
{ fprintf(stderr, "fromwire_onchain_htlc called!\n"); abort(); }
|
||||
/* Generated stub for fromwire_onchain_init */
|
||||
bool fromwire_onchain_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, u64 *funding_amount_satoshi UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *feerate_per_kw UNNEEDED, u64 *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *funder UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct bitcoin_tx **tx UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, secp256k1_ecdsa_signature **htlc_signature UNNEEDED, u64 *num_htlcs UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED)
|
||||
bool fromwire_onchain_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, u64 *funding_amount_satoshi UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *feerate_per_kw UNNEEDED, u64 *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *funder UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct bitcoin_tx **tx UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, secp256k1_ecdsa_signature **htlc_signature UNNEEDED, u64 *num_htlcs UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED)
|
||||
{ fprintf(stderr, "fromwire_onchain_init called!\n"); abort(); }
|
||||
/* Generated stub for fromwire_onchain_known_preimage */
|
||||
bool fromwire_onchain_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED)
|
||||
|
|
|
@ -1141,7 +1141,7 @@ def test_funder_feerate_reconnect(node_factory, bitcoind):
|
|||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "needs LIGHTNINGD_DEV_LOG_IO")
|
||||
def test_dataloss_protection(node_factory):
|
||||
def test_dataloss_protection(node_factory, bitcoind):
|
||||
l1 = node_factory.get_node(may_reconnect=True, log_all_io=True)
|
||||
l2 = node_factory.get_node(may_reconnect=True, log_all_io=True)
|
||||
|
||||
|
@ -1214,4 +1214,14 @@ def test_dataloss_protection(node_factory):
|
|||
l2.daemon.wait_for_log("Cannot broadcast our commitment tx: they have a future one")
|
||||
assert not l2.daemon.is_in_log('sendrawtx exit 0')
|
||||
|
||||
# FIXME: l2 should still be able to collect onchain.
|
||||
closetxid = only_one(bitcoind.rpc.getrawmempool(False))
|
||||
|
||||
# l2 should still recover something!
|
||||
bitcoind.generate_block(1)
|
||||
|
||||
l2.daemon.wait_for_log("ERROR: Unknown commitment #2, recovering our funds!")
|
||||
bitcoind.generate_block(100)
|
||||
l2.daemon.wait_for_log('WIRE_ONCHAIN_ALL_IRREVOCABLY_RESOLVED')
|
||||
|
||||
# l2 should have it in wallet.
|
||||
assert (closetxid, "confirmed") in set([(o['txid'], o['status']) for o in l2.rpc.listfunds()['outputs']])
|
||||
|
|
Loading…
Add table
Reference in a new issue