bkpr-zeroconf: Zeroconfs will emit 'channel_proposed' event

Keep the accounts as an 'append only' log, instead we move the marker
for the 'channel_open' forward when a 'channel_open' comes out.

We also neatly hide the 'channel_proposed' events in 'inspect' if
there's a 'channel_open' for that same event.

If you call inspect before the 'channel_open' is confirmed, you'll see
the tag as 'channel_proposed', afterwards it shows up as
'channel_open'. However the event log rolls forward -- listaccountevents
will show the correct history of the proposal then open confirming (plus
any routing that happened before the channel confirmed).
This commit is contained in:
niftynei 2022-07-27 16:18:05 -05:00 committed by Rusty Russell
parent 30aa1d79fb
commit e048292fdf
5 changed files with 71 additions and 16 deletions

View File

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

View File

@ -11,6 +11,7 @@ BOOKKEEPER_TEST_COMMON_OBJS := \
common/base32.o \
common/blockheight_states.o \
common/channel_type.o \
common/coin_mvt.o \
common/features.o \
common/json_stream.o \
common/key_derive.o \

View File

@ -88,6 +88,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* Generated stub for fromwire_u8_array */
void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
/* Generated stub for fromwire_wirestring */
char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); }
/* Generated stub for htlc_state_flags */
int htlc_state_flags(enum htlc_state state UNNEEDED)
{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); }
@ -177,9 +180,6 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED)
/* Generated stub for log_level_name */
const char *log_level_name(enum log_level level UNNEEDED)
{ fprintf(stderr, "log_level_name called!\n"); abort(); }
/* Generated stub for mvt_tag_str */
const char *mvt_tag_str(enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); }
/* Generated stub for new_channel_event */
struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED,
const char *tag UNNEEDED,
@ -225,6 +225,9 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
/* Generated stub for towire_u8_array */
void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
/* Generated stub for towire_wirestring */
void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED)
{ fprintf(stderr, "towire_wirestring called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static char *tmp_dsn(const tal_t *ctx)

View File

@ -94,6 +94,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* Generated stub for fromwire_u8_array */
void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
/* Generated stub for fromwire_wirestring */
char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); }
/* Generated stub for htlc_state_flags */
int htlc_state_flags(enum htlc_state state UNNEEDED)
{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); }
@ -183,9 +186,6 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED)
/* Generated stub for log_level_name */
const char *log_level_name(enum log_level level UNNEEDED)
{ fprintf(stderr, "log_level_name called!\n"); abort(); }
/* Generated stub for mvt_tag_str */
const char *mvt_tag_str(enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); }
/* Generated stub for new_channel_event */
struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED,
const char *tag UNNEEDED,
@ -231,6 +231,9 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
/* Generated stub for towire_u8_array */
void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
/* Generated stub for towire_wirestring */
void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED)
{ fprintf(stderr, "towire_wirestring called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static char *tmp_dsn(const tal_t *ctx)

View File

@ -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,6 +1344,7 @@ 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)
@ -1351,10 +1354,22 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams):
chan_val = 993198000 if chainparams['elements'] else 995673000
l1_mvts = [
{'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')
@ -1364,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': 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.
@ -1378,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.