-
Notifications
You must be signed in to change notification settings - Fork 906
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
Add a 'reservation expiration' mechanism #3789
Conversation
" WHERE status = ?" | ||
" AND reserved_til <= ?")); | ||
db_bind_int(stmt, 0, output_status_in_db(output_state_reserved)); | ||
db_bind_int(stmt, 1, w->ld->topology->tip->height); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to pass in the current blockheight explicitly? Could make it a bit more explicit that expirations are blockheight based.
reserved = expired_reserved_utxos_get(ctx, w); | ||
available = wallet_get_utxos(ctx, w, output_state_available); | ||
|
||
rcount = tal_count(reserved); | ||
tal_resize(&reserved, rcount + tal_count(available)); | ||
for (size_t i = 0; i < tal_count(available); i++) | ||
reserved[rcount + i] = tal_steal(reserved, available[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we rather just expire the reserved ones, and then query them directly as output_state_available
? This way we keep the reservation but are also returning them as available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes but then we'd have to clean them up on every new block. Another option would be to remove the 'output_state_available'/ status altogether and just use the reserved_til as a marker of whether or not the utxo is still reserved.
wallet/walletrpc.c
Outdated
* or 24 blocks */ | ||
u32 expires_at = cmd->ld->topology->tip->height + 6 * 4; | ||
u32 expires_at = cmd->ld->topology->tip->height + 6 * 24; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit message needs an update if we change this default value.
wallet/walletrpc.c
Outdated
for (size_t i = 0; i < tal_count(utx->wtx->utxos); i++) | ||
wallet_output_reservation_update(cmd->ld->wallet, | ||
utx->wtx->utxos[i], | ||
*expire_at); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to me like this uses the relative expiry as absolute blockheight. Should probably be something like the following:
for (size_t i = 0; i < tal_count(utx->wtx->utxos); i++) | |
wallet_output_reservation_update(cmd->ld->wallet, | |
utx->wtx->utxos[i], | |
*expire_at); | |
for (size_t i = 0; i < tal_count(utx->wtx->utxos); i++) | |
wallet_output_reservation_update(cmd->ld->wallet, | |
utx->wtx->utxos[i], | |
cmd->ld->topology->tip->height + *expire_at); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, good spot.
if (!*utxos) | ||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, | ||
"Could not decode all of the outpoints. The utxos" | ||
" should be specified as an array of " | ||
" 'txid:output_index'."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like an unrelated change.
tests/test_wallet.py
Outdated
unreserve_psbt = bitcoind.rpc.createpsbt(unreserve_utxos, []) | ||
unres = l1.rpc.unreserveinputs(unreserve_psbt) | ||
|
||
# let's not set the fee high enough, should explodes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# let's not set the fee high enough, should explodes | |
# let's not set the fee high enough, should explode |
l1.rpc.sendpsbt(signed_psbt) | ||
assert bitcoind.rpc.getmempoolinfo()['size'] == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check that the new tx is in the mempool? Just checking for the size of the mempool might pass despite the old tx still being in there. bitcoind.rpc.getrawmempool()
will give you the txids.
Note: after discussing with @niftynei I agree with this concept, but she didn't take it far enough. Because we didn't expire utxo reservations, we had multiple kludges:
On digging further, I noticed that with a simple change, we can move utxo.h entirely into the wallet dir. This further allows us to simplify our utxo handling. So I've cherry picked part of the "reservations expire" commit and started ripping things apart... |
bitcoin/psbt.c
Outdated
tmp_in = tx->inputs[tx->num_inputs - 1]; | ||
MAKE_ROOM(tx->inputs, insert_at, tx->num_inputs); | ||
|
||
tx->inputs[insert_at] = tmp_in; | ||
|
||
|
||
if (psbt->inputs_allocation_len < tx->num_inputs) { | ||
struct wally_psbt_input *p = tal_arr(psbt, struct wally_psbt_input, tx->num_inputs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commit cf4054f is empty except this whitespace change, remove
wallet/wallet.c
Outdated
if (!db_column_is_null(stmt, 12)) { | ||
reserved_til = tal(utxo, u32); | ||
*reserved_til = db_column_int(stmt, 12); | ||
utxo->reserved_til = reserved_til; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do this as:
u32 reserved_til = db_column_int(stmt, 12);
utxo->reserved_til = tal_dup(utxo, u32, &reserved_til);
cd392cb
to
84b0287
Compare
Allow a utxo to be reserved until explicitly unreserved or until a timer runs out. Currently unused
Check that the reservations stick around after a node restarts
If a utxo is unspent, don't remove the reservation unless it's expired (or no expiration was set on it).
we don't pro-actively 'update' the state of a reserved utxo to available when its lease expired, instead we compute whether or not it's now considered eligible for spending, which is what the 'reserve' flag here really is asking
if you haven't used this lease in 4 hours, it'll be eligible to be used in a different utxo.
Now `reserveinputs` can parse its params on its own, which will let us add more specific params for this command
Changelog-Added: JSON-API: `reserveinputs` has a new parameter, `expire_after`, which can be used to adjust or disable the reservation expiration mechansim (setting it to zero disables it)
Changelog-Added: JSON-API: `reserveinputs` has a new parameter, `allow_rbf`, which will make a RBF'able tx. Defaults to true
Adds an RBF test plus associated utxo state tracking fixes needed to make this possible. Mostly, you *must* specify which inputs you're spending when you attempt to RBF (makes sense) -- these inputs are allowed to be marked as 'spent' (which means we've broadcast a tx spending them) but must not be included in a block yet (which would be indicated by the spendheight) being populated. There's a few other places that 'spending' already spent tx's is problematic, e.g. when we mark a utxo as spent we now allow it to be coming from 'any' status -> spent. We also allow psbtsign to pull up any 'already spent' utxos to be signed again
84b0287
to
b6e759c
Compare
This will largely be subsumed by work @rustyrussell is doing on the utxo infrastructure, but updated with comments for good measure. |
Thanks for the heads up 👍 |
I thought this was #3798 , which was withdrawn? The nearest equivalent is #3821, which does not look like it has reservation expiration. What is the status? How does this affect #3763 which apparently need to be blocked for the new UTXO things? |
It's useful to be able to 'retire' reservations on utxos after a certain time lapse. It's also useful to be able to RBF transactions. This PR lets us do both now, by adding two new parameters to
reserveinputs
.expire_after
which will functionally 'unreserve' a utxo after that many blocks have passed, andallow_rbf
which if set will create a psbt that has all of its inputs set to be rbf'able.RBF'ability forced a few not so nice state breaks in the output status (you have to be able to select utxos that are marked as 'spent' but not in a block yet, etc). But hey, RBF, here it is!
Built ontop of the as of yet unmerged #3775 (new commits start at 37ba229)