Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bkpr tests: return actual error data, not just a blank assert failure #8051

Merged
merged 2 commits into from
Feb 7, 2025
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
26 changes: 6 additions & 20 deletions tests/test_closing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
account_balance, first_channel_id, closing_fee, TEST_NETWORK,
scriptpubkey_addr, calc_lease_fee,
check_utxos_channel, check_coin_moves,
check_balance_snaps, mine_funding_to_announce, check_inspect_channel,
mine_funding_to_announce, check_inspect_channel,
first_scid
)

Expand Down Expand Up @@ -1311,7 +1311,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors):
l2.daemon.wait_for_log('{}.*: onchaind complete, forgetting peer'.format(l3.info['id']))

assert account_balance(l3, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# we can't check the account balance on l2 because it goes negative

expected_2 = {
'A': [('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')],
Expand All @@ -1332,25 +1332,11 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors):
expected_3['B'].append(('external', ['anchor'], None, None))
expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None))
expected_3['B'].append(('wallet', ['anchor', 'ignored'], None, None))
# We RBF spend the HTLC tx, which creates a new deposit
expected_2['C'].append(('wallet', ['deposit'], None, None))

# FIXME: Why does this fail?
if not anchors:
tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id)
check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id)

if not chainparams['elements']:
# Also check snapshots
expected_bals_2 = [
{'blockheight': 101, 'accounts': [
{'balance_msat': '0msat', 'account_id': 'wallet'}
]}
] + [
{'blockheight': 108, 'accounts': [
{'balance_msat': '995073000msat', 'account_id': 'wallet'},
{'balance_msat': '500000000msat', 'account_id': first_channel_id(l1, l2)},
{'balance_msat': '499994999msat', 'account_id': channel_id}]}
] * 2 # duplicated; we stop and restart l2 twice (both at block 108)
check_balance_snaps(l2, expected_bals_2)
tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id)
check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id)


@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db")
Expand Down
90 changes: 63 additions & 27 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ def _dictify(balances):
def check_balance_snaps(n, expected_bals):
snaps = n.rpc.listsnapshots()['balance_snapshots']
for snap, exp in zip(snaps, expected_bals):
assert snap['blockheight'] == exp['blockheight']
if snap['blockheight'] != exp['blockheight']:
raise Exception('Unexpected balance snap blockheight: {} vs {}'.format(_dictify(snap), _dictify(exp)))
if _dictify(snap) != _dictify(exp):
raise Exception('Unexpected balance snap: {} vs {}'.format(_dictify(snap), _dictify(exp)))

Expand All @@ -136,20 +137,26 @@ def check_coin_moves(n, account_id, expected_moves, chainparams):

node_id = n.info['id']
acct_moves = [m for m in moves if m['account_id'] == account_id]
# Stash moves for errors, if needed
_acct_moves = acct_moves
for mv in acct_moves:
print("{{'type': '{}', 'credit_msat': {}, 'debit_msat': {}, 'tags': '{}' , ['fees_msat'?: '{}']}},"
.format(mv['type'],
Millisatoshi(mv['credit_msat']).millisatoshis,
Millisatoshi(mv['debit_msat']).millisatoshis,
mv['tags'],
mv['fees_msat'] if 'fees_msat' in mv else ''))
assert mv['version'] == 2
assert mv['node_id'] == node_id
assert mv['timestamp'] > 0
assert mv['coin_type'] == chainparams['bip173_prefix']
if mv['version'] != 2:
raise ValueError(f'version not 2 {mv}')
if mv['node_id'] != node_id:
raise ValueError(f'node_id not: {mv}')
if mv['timestamp'] <= 0:
raise ValueError(f'timestamp invalid: {mv}')
if mv['coin_type'] != chainparams['bip173_prefix']:
raise ValueError(f'coin_type not {chainparams["bip173_prefix"]}: {mv}')
# chain moves should have blockheights
if mv['type'] == 'chain_mvt' and mv['account_id'] != 'external':
assert mv['blockheight'] is not None
if mv['type'] == 'chain_mvt' and mv['account_id'] != 'external' and 'blockheight' not in mv:
raise ValueError(f'blockheight not set: {mv}')

for num, m in enumerate(expected_moves):
# They can group things which are in any order.
Expand All @@ -170,13 +177,15 @@ def check_coin_moves(n, account_id, expected_moves, chainparams):
raise ValueError("Unexpected move {}: {} != {}".format(num, acct_moves[0], m))
acct_moves = acct_moves[1:]

assert acct_moves == []
if acct_moves != []:
raise ValueError(f'check_coin_moves failed: still has acct_moves {acct_moves}. exp: {expected_moves}. actual: {_acct_moves}')


def account_balance(n, account_id):
moves = dedupe_moves(n.rpc.call('listcoinmoves_plugin')['coin_moves'])
chan_moves = [m for m in moves if m['account_id'] == account_id]
assert len(chan_moves) > 0
if len(chan_moves) == 0:
raise ValueError(f"No channel moves found for {account_id}. {moves}")
m_sum = Millisatoshi(0)
for m in chan_moves:
m_sum += Millisatoshi(m['credit_msat'])
Expand All @@ -201,7 +210,8 @@ def extract_utxos(moves):
for ev in evs:
if ev[0]['vout'] == m['vout']:
ev[1] = m
assert ev[0]['output_msat'] == m['output_msat']
if ev[0]['output_msat'] != m['output_msat']:
raise ValueError(f'output_msat does not match. expected {ev[0]}. actual {m}')
break
return utxos

Expand Down Expand Up @@ -246,9 +256,14 @@ def _add_relevant(txid, utxo):


def matchup_events(u_set, evs, chans, tag_list):
assert len(u_set) == len(evs) and len(u_set) > 0
if len(u_set) != len(evs):
raise ValueError(f"utxo-set does not match expected (diff lens). exp {evs}, actual {u_set}")
if len(u_set) == 0:
raise ValueError(f"utxo-set is empty. exp {evs}, actual {u_set}")

txid = u_set[0][0]['utxo_txid']
# Stash the set for logging at end, if error
_u_set = u_set
for ev in evs:
found = False
for u in u_set:
Expand All @@ -266,15 +281,17 @@ def matchup_events(u_set, evs, chans, tag_list):
continue

if ev[2] is None:
assert u[1] is None
if u[1] is not None:
raise ValueError(f"Expected unspent utxo. exp {ev}, actual {u}")
found = True
u_set.remove(u)
break

# ugly hack to annotate two possible futures for a utxo
if type(ev[2]) is tuple:
tag = u[1]['tags'] if u[1] else u[1]
assert tag in [x[0] for x in ev[2]]
if tag not in [x[0] for x in ev[2]]:
raise ValueError(f"Unable to find {tag} in event set {ev}")
if not u[1]:
found = True
u_set.remove(u)
Expand All @@ -284,18 +301,22 @@ def matchup_events(u_set, evs, chans, tag_list):
# Save the 'spent to' txid in the tag-list
tag_list[x[1]] = u[1]['txid']
else:
assert ev[2] == u[1]['tags']
if ev[2] != u[1]['tags']:
raise ValueError(f"tags dont' match. exp {ev}, actual ({u[1]}) full utxo info: {u}")
# Save the 'spent to' txid in the tag-list
if 'to_miner' not in u[1]['tags']:
tag_list[ev[3]] = u[1]['txid']

found = True
u_set.remove(u)

assert found
if not found:
raise ValueError(f"Unable to find expected event in utxos. exp {ev}, utxos {u_set}")

# Verify we used them all up
assert len(u_set) == 0
if len(u_set) != 0:
raise ValueError(f"Too many utxo events. exp {ev}, actual {_u_set} (extra: {u_set})")

return txid


Expand All @@ -316,7 +337,9 @@ def dedupe_moves(moves):


def inspect_check_actual(txids, channel_id, actual, exp):
assert len(actual['outputs']) == len(exp)
if len(actual['outputs']) != len(exp):
raise ValueError(f"actual outputs != exp. exp: {exp}. actual: {actual['outputs']}")

for e in exp:
# find the event in actual that matches
found = False
Expand All @@ -330,20 +353,26 @@ def inspect_check_actual(txids, channel_id, actual, exp):
if e[1][0] != a['output_tag']:
continue
if e[2]:
assert e[2][0] == a['spend_tag']
if e[2][0] != a['spend_tag']:
raise ValueError(f'spend_tag mismatch. expected: {e}, actual {a}')
txids.append((e[3], a['spending_txid']))
else:
assert 'spend_tag' not in a
elif 'spend_tag' in a:
raise ValueError(f'{a} contains "spend_tag", expecting {e}')

found = True
break
assert found
if not found:
raise ValueError(f'Unable to find actual tx {a} in expected {exp}')

return txids


def check_inspect_channel(n, channel_id, expected_txs):
actual_txs = n.rpc.bkpr_inspect(channel_id)['txs']
assert len(actual_txs) == len(expected_txs.keys())
# Stash a copy in case we need to print on error/assert at end
_actual_txs = actual_txs
if len(actual_txs) != len(expected_txs.keys()):
raise ValueError(f'count actual_txs != expected exp: {expected_txs}')
# start at the top
exp = list(expected_txs.values())[0]
actual = actual_txs[0]
Expand All @@ -360,7 +389,8 @@ def check_inspect_channel(n, channel_id, expected_txs):
if a['txid'] == txid:
actual = a
break
assert actual
if not actual:
raise ValueError(f'No "actual" tx found, looking for {txid}. {actual_txs}')
exp = expected_txs[marker]
inspect_check_actual(txids, channel_id, actual, exp)

Expand All @@ -369,8 +399,10 @@ def check_inspect_channel(n, channel_id, expected_txs):
exp_counter += 1

# Did we inspect everything?
assert len(actual_txs) == 0
assert exp_counter == len(expected_txs.keys())
if len(actual_txs) != 0:
raise ValueError(f'Had more txs than expected. expected: {expected_txs}. actual: {_actual_txs}')
if exp_counter != len(expected_txs.keys()):
raise ValueError(f'Had less txs than expected. expected: {expected_txs}. actual txs: {_actual_txs}')


def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=None):
Expand All @@ -382,6 +414,8 @@ def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=No
if filter_channel:
utxos = utxos_for_channel(utxos, filter_channel)

# Stash for errors, if we get them
_utxos = utxos
for tag, evs in expected.items():
if tag not in tag_list:
u_set = list(utxos.values())[0]
Expand All @@ -397,13 +431,15 @@ def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=No
del utxos[txid]

# Verify that we went through all of the utxos
assert len(utxos) == 0
if len(utxos) != 0:
raise ValueError(f"leftover utxos? expected: {expected}, actual: {_utxos}")

# Verify that the expected tags match the found tags
if exp_tag_list:
for tag, txid in tag_list.items():
if tag in exp_tag_list:
assert exp_tag_list[tag] == txid
if exp_tag_list[tag] != txid:
raise ValueError(f"expected tags txid {exp_tag_list[tag]} != actual {txid}. expected: {exp_tag_list}, actual: {tag_list}")

return tag_list

Expand Down