core-lightning/wallet/reservation.c
Rusty Russell 56ea215ba0 wallet: new JSON commands reserveinputs and unreserveinputs.
reserveinputs marks UTXOs reserved for 12 hours, so we won't select them
for spending: unreserveinputs marks them available again.

Exposes param_psbt() for wider use.

Disabled the test_sign_and_send_psbt since we're altering the API;
the final patch restores it.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2020-07-15 18:49:02 +09:30

155 lines
4.3 KiB
C

/* Dealing with reserving UTXOs */
#include <common/json_command.h>
#include <common/json_helpers.h>
#include <common/jsonrpc_errors.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
#include <wallet/wallet.h>
#include <wallet/walletrpc.h>
static bool was_reserved(enum output_status oldstatus,
const u32 *reserved_til,
u32 current_height)
{
if (oldstatus != output_state_reserved)
return false;
return *reserved_til > current_height;
}
static void json_add_reservestatus(struct json_stream *response,
const struct utxo *utxo,
enum output_status oldstatus,
u32 old_res,
u32 current_height)
{
json_object_start(response, NULL);
json_add_txid(response, "txid", &utxo->txid);
json_add_u32(response, "vout", utxo->outnum);
json_add_bool(response, "was_reserved",
was_reserved(oldstatus, &old_res, current_height));
json_add_bool(response, "reserved",
is_reserved(utxo, current_height));
if (utxo->reserved_til)
json_add_u32(response, "reserved_to_block",
*utxo->reserved_til);
json_object_end(response);
}
static struct command_result *json_reserveinputs(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct wally_psbt *psbt;
struct utxo **utxos = tal_arr(cmd, struct utxo *, 0);
if (!param(cmd, buffer, params,
p_req("psbt", param_psbt, &psbt),
NULL))
return command_param_failed();
for (size_t i = 0; i < psbt->tx->num_inputs; i++) {
struct bitcoin_txid txid;
struct utxo *utxo;
wally_tx_input_get_txid(&psbt->tx->inputs[i], &txid);
utxo = wallet_utxo_get(cmd, cmd->ld->wallet,
&txid, psbt->tx->inputs[i].index);
if (!utxo)
continue;
if (utxo->status == output_state_spent)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"%s:%u already spent",
type_to_string(tmpctx,
struct bitcoin_txid,
&utxo->txid),
utxo->outnum);
tal_arr_expand(&utxos, utxo);
}
response = json_stream_success(cmd);
json_array_start(response, "reservations");
for (size_t i = 0; i < tal_count(utxos); i++) {
enum output_status oldstatus;
u32 old_res;
oldstatus = utxos[i]->status;
old_res = utxos[i]->reserved_til ? *utxos[i]->reserved_til : 0;
if (!wallet_reserve_utxo(cmd->ld->wallet,
utxos[i],
get_block_height(cmd->ld->topology))) {
fatal("Unable to reserve %s:%u!",
type_to_string(tmpctx,
struct bitcoin_txid,
&utxos[i]->txid),
utxos[i]->outnum);
}
json_add_reservestatus(response, utxos[i], oldstatus, old_res,
get_block_height(cmd->ld->topology));
}
json_array_end(response);
return command_success(cmd, response);
}
static const struct json_command reserveinputs_command = {
"reserveinputs",
"bitcoin",
json_reserveinputs,
"Reserve utxos (or increase their reservation)",
false
};
AUTODATA(json_command, &reserveinputs_command);
static struct command_result *json_unreserveinputs(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct wally_psbt *psbt;
if (!param(cmd, buffer, params,
p_req("psbt", param_psbt, &psbt),
NULL))
return command_param_failed();
response = json_stream_success(cmd);
json_array_start(response, "reservations");
for (size_t i = 0; i < psbt->tx->num_inputs; i++) {
struct bitcoin_txid txid;
struct utxo *utxo;
enum output_status oldstatus;
u32 old_res;
wally_tx_input_get_txid(&psbt->tx->inputs[i], &txid);
utxo = wallet_utxo_get(cmd, cmd->ld->wallet,
&txid, psbt->tx->inputs[i].index);
if (!utxo || utxo->status != output_state_reserved)
continue;
oldstatus = utxo->status;
old_res = *utxo->reserved_til;
wallet_unreserve_utxo(cmd->ld->wallet,
utxo,
get_block_height(cmd->ld->topology));
json_add_reservestatus(response, utxo, oldstatus, old_res,
get_block_height(cmd->ld->topology));
}
json_array_end(response);
return command_success(cmd, response);
}
static const struct json_command unreserveinputs_command = {
"unreserveinputs",
"bitcoin",
json_unreserveinputs,
"Unreserve utxos (or at least, reduce their reservation)",
false
};
AUTODATA(json_command, &unreserveinputs_command);