diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 2eb500c5e6a8..f2a0f4f4c777 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -225,7 +225,8 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, " ORDER BY " " e.utxo_txid" ", e.outnum" - ", e.spending_txid NULLS FIRST")); + ", e.spending_txid NULLS FIRST" + ", e.blockheight")); db_bind_txid(stmt, 0, txid); return find_chain_events(ctx, take(stmt)); @@ -403,8 +404,14 @@ static struct txo_set *find_txo_set(const tal_t *ctx, } else { /* We might not have a spend event * for everything */ - if (pr) - tal_arr_expand(&txos->pairs, pr); + if (pr) { + /* Disappear "channel_proposed" events */ + if (streq(pr->txo->tag, + mvt_tag_str(CHANNEL_PROPOSED))) + pr = tal_free(pr); + else + tal_arr_expand(&txos->pairs, pr); + } pr = new_txo_pair(txos->pairs); pr->txo = tal_steal(pr, ev); } @@ -671,7 +678,8 @@ static struct chain_event *find_chain_event(const tal_t *ctx, struct db *db, const struct account *acct, const struct bitcoin_outpoint *outpoint, - const struct bitcoin_txid *spending_txid) + const struct bitcoin_txid *spending_txid, + const char *tag) { struct db_stmt *stmt; @@ -733,7 +741,10 @@ static struct chain_event *find_chain_event(const tal_t *ctx, " e.account_id = ?" " AND e.utxo_txid = ?" " AND e.outnum = ?" - " AND e.spending_txid IS NULL")); + " AND e.spending_txid IS NULL" + " AND e.tag = ?")); + + db_bind_text(stmt, 3, tag); } db_bind_u64(stmt, 0, acct->db_id); @@ -1850,7 +1861,8 @@ bool log_chain_event(struct db *db, /* We're responsible for de-duping chain events! */ if (find_chain_event(e, db, acct, - &e->outpoint, e->spending_txid)) + &e->outpoint, e->spending_txid, + e->tag)) return false; stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" diff --git a/tests/test_opening.py b/tests/test_opening.py index 68a1a788785a..96db12fc08c7 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1331,9 +1331,11 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): }, {} ]) + # Advances blockheight to 102 l1.fundwallet(10**6) + push_msat = 20000 * 1000 l1.connect(l2) - l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0) + l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0, push_msat=push_msat) # Wait for the update to be signed (might not be the most reliable # signal) @@ -1342,18 +1344,32 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + channel_id = l1chan['channel_id'] # We have no confirmation yet, so no `short_channel_id` assert('short_channel_id' not in l1chan) assert('short_channel_id' not in l2chan) # Channel is "proposed" + chan_val = 993198000 if chainparams['elements'] else 995673000 l1_mvts = [ - {'type': 'chain_mvt', 'credit_msat': 995673000, 'debit_msat': 0, 'tags': ['channel_proposed', 'opener']}, + {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_proposed', 'opener']}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 20000000, 'tags': ['pushed'], 'fees_msat': '0msat'}, ] check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) - # Now add 1 confirmation, we should get a `short_channel_id` + # Check that the channel_open event has blockheight of zero + for n in [l1, l2]: + evs = n.rpc.bkpr_listaccountevents(channel_id)['events'] + open_ev = only_one([e for e in evs if e['tag'] == 'channel_proposed']) + assert open_ev['blockheight'] == 0 + + # Call inspect, should have pending event in it + tx = only_one(n.rpc.bkpr_inspect(channel_id)['txs']) + assert 'blockheight' not in tx + assert only_one(tx['outputs'])['output_tag'] == 'channel_proposed' + + # Now add 1 confirmation, we should get a `short_channel_id` (block 103) bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') l2.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') @@ -1363,11 +1379,24 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): assert('short_channel_id' in l1chan) assert('short_channel_id' in l2chan) - # We also now have an 'open' event + # We also now have an 'open' event, the push event isn't re-recorded l1_mvts += [ - {'type': 'chain_mvt', 'credit_msat': 995673000, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, + {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, ] - check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) + check_coin_moves(l1, channel_id, l1_mvts, chainparams) + + # Check that there is a channel_open event w/ real blockheight + for n in [l1, l2]: + evs = n.rpc.bkpr_listaccountevents(channel_id)['events'] + # Still has the channel-proposed event + only_one([e for e in evs if e['tag'] == 'channel_proposed']) + open_ev = only_one([e for e in evs if e['tag'] == 'channel_open']) + assert open_ev['blockheight'] == 103 + + # Call inspect, should have open event in it + tx = only_one(n.rpc.bkpr_inspect(channel_id)['txs']) + assert tx['blockheight'] == 103 + assert only_one(tx['outputs'])['output_tag'] == 'channel_open' # Now make it public, we should be switching over to the real # scid. @@ -1377,6 +1406,14 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l3.connect(l1) wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) + # Close the zerconf channel, check that we mark it as onchain_resolved ok + l1.rpc.close(l2.info['id']) + bitcoind.generate_block(1, wait_for_mempool=1) + + # Channel should be marked resolved + for n in [l1, l2]: + wait_for(lambda: only_one([x for x in n.rpc.bkpr_listbalances()['accounts'] if x['account'] == channel_id])['account_resolved']) + def test_zeroconf_forward(node_factory, bitcoind): """Ensure that we can use zeroconf channels in forwards.