mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 22:45:27 +01:00
plugins/bcli.c: sendrawtransaction
now has a required allowhighfees
argument.
Changelog-Deprecated: plugin: `bcli` replacements should note that `sendrawtransaction` now has a second required Boolean argument, `allowhighfees`, which if `true`, means ignore any fee limits and just broadcast the transaction. Use `--deprecated-apis` to use older `bcli` replacement plugins that only support a single argument.
This commit is contained in:
parent
12c9b27838
commit
ee276bcb86
8 changed files with 162 additions and 17 deletions
|
@ -1179,8 +1179,11 @@ The plugin must respond to `gettxout` with the following fields:
|
||||||
|
|
||||||
### `sendrawtransaction`
|
### `sendrawtransaction`
|
||||||
|
|
||||||
This call takes one parameter, a string representing a hex-encoded Bitcoin
|
This call takes two parameters,
|
||||||
transaction.
|
a string `tx` representing a hex-encoded Bitcoin transaction,
|
||||||
|
and a boolean `allowhighfees`, which if set means suppress
|
||||||
|
any high-fees check implemented in the backend, since the given
|
||||||
|
transaction may have fees that are very high.
|
||||||
|
|
||||||
The plugin must broadcast it and respond with the following fields:
|
The plugin must broadcast it and respond with the following fields:
|
||||||
- `success` (boolean), which is `true` if the broadcast succeeded
|
- `success` (boolean), which is `true` if the broadcast succeeded
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <ccan/tal/grab_file/grab_file.h>
|
#include <ccan/tal/grab_file/grab_file.h>
|
||||||
#include <ccan/tal/path/path.h>
|
#include <ccan/tal/path/path.h>
|
||||||
#include <ccan/tal/str/str.h>
|
#include <ccan/tal/str/str.h>
|
||||||
|
#include <common/configdir.h>
|
||||||
#include <common/json_helpers.h>
|
#include <common/json_helpers.h>
|
||||||
#include <common/memleak.h>
|
#include <common/memleak.h>
|
||||||
#include <common/timeout.h>
|
#include <common/timeout.h>
|
||||||
|
@ -252,6 +253,7 @@ void bitcoind_estimate_fees_(struct bitcoind *bitcoind,
|
||||||
|
|
||||||
struct sendrawtx_call {
|
struct sendrawtx_call {
|
||||||
struct bitcoind *bitcoind;
|
struct bitcoind *bitcoind;
|
||||||
|
const char *hextx;
|
||||||
void (*cb)(struct bitcoind *bitcoind,
|
void (*cb)(struct bitcoind *bitcoind,
|
||||||
bool success,
|
bool success,
|
||||||
const char *err_msg,
|
const char *err_msg,
|
||||||
|
@ -293,28 +295,113 @@ static void sendrawtx_callback(const char *buf, const jsmntok_t *toks,
|
||||||
tal_free(call);
|
tal_free(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
|
/* For compatibility with `sendrawtransaction` plugins written for
|
||||||
const char *hextx,
|
* 0.9.0 or earlier.
|
||||||
void (*cb)(struct bitcoind *bitcoind,
|
* Once this deprecated API is removed, we can just delete this function
|
||||||
bool success, const char *err_msg, void *),
|
* and use sendrawtx_callback directly in bitcoind_sendrawtx_ahf_.
|
||||||
void *cb_arg)
|
*/
|
||||||
|
static void sendrawtx_compatv090_callback(const char *buf,
|
||||||
|
const jsmntok_t *toks,
|
||||||
|
const jsmntok_t *idtok,
|
||||||
|
struct sendrawtx_call *call)
|
||||||
|
{
|
||||||
|
const jsmntok_t *errtok;
|
||||||
|
const jsmntok_t *codetok;
|
||||||
|
errcode_t code;
|
||||||
|
struct jsonrpc_request *req;
|
||||||
|
|
||||||
|
static bool warned = false;
|
||||||
|
|
||||||
|
|
||||||
|
/* If deprecated APIs not enabled, fail it outright. */
|
||||||
|
if (!deprecated_apis)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
/* If we got a JSONRPC2_INVALID_PARAMS error, assume it is
|
||||||
|
* because it is an old plugin which does not support the
|
||||||
|
* new `allowhighfees` parameter.
|
||||||
|
*/
|
||||||
|
errtok = json_get_member(buf, toks, "error");
|
||||||
|
if (!errtok)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
codetok = json_get_member(buf, errtok, "code");
|
||||||
|
if (!codetok)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
if (!json_to_errcode(buf, codetok, &code))
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
if (code != JSONRPC2_INVALID_PARAMS)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
/* Possibly it is because `allowhighfees` is not understood
|
||||||
|
* by the plugin. */
|
||||||
|
if (!warned) {
|
||||||
|
warned = true;
|
||||||
|
log_unusual(call->bitcoind->log,
|
||||||
|
"`sendrawtransaction` failed when given two "
|
||||||
|
"arguments, will retry with single-argument "
|
||||||
|
"deprecated API.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retry with a single argument, hextx. */
|
||||||
|
req = jsonrpc_request_start(call->bitcoind, "sendrawtransaction",
|
||||||
|
call->bitcoind->log,
|
||||||
|
&sendrawtx_callback, call);
|
||||||
|
json_add_string(req->stream, "tx", call->hextx);
|
||||||
|
jsonrpc_request_end(req);
|
||||||
|
bitcoin_plugin_send(call->bitcoind, req);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
sendrawtx_callback(buf, toks, idtok, call);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind,
|
||||||
|
const char *hextx,
|
||||||
|
bool allowhighfees,
|
||||||
|
void (*cb)(struct bitcoind *bitcoind,
|
||||||
|
bool success, const char *msg, void *),
|
||||||
|
void *cb_arg)
|
||||||
{
|
{
|
||||||
struct jsonrpc_request *req;
|
struct jsonrpc_request *req;
|
||||||
struct sendrawtx_call *call = tal(bitcoind, struct sendrawtx_call);
|
struct sendrawtx_call *call = tal(bitcoind, struct sendrawtx_call);
|
||||||
|
|
||||||
call->bitcoind = bitcoind;
|
call->bitcoind = bitcoind;
|
||||||
|
/* For compatibility with 0.9.0 or earlier.
|
||||||
|
* Once removed, we can remove the hextx field in
|
||||||
|
* struct sendrawtx_call as well.
|
||||||
|
*/
|
||||||
|
if (deprecated_apis)
|
||||||
|
call->hextx = tal_strdup(call, hextx);
|
||||||
|
else
|
||||||
|
call->hextx = NULL;
|
||||||
call->cb = cb;
|
call->cb = cb;
|
||||||
call->cb_arg = cb_arg;
|
call->cb_arg = cb_arg;
|
||||||
log_debug(bitcoind->log, "sendrawtransaction: %s", hextx);
|
log_debug(bitcoind->log, "sendrawtransaction: %s", hextx);
|
||||||
|
|
||||||
req = jsonrpc_request_start(bitcoind, "sendrawtransaction",
|
req = jsonrpc_request_start(bitcoind, "sendrawtransaction",
|
||||||
bitcoind->log, sendrawtx_callback,
|
bitcoind->log,
|
||||||
|
sendrawtx_compatv090_callback,
|
||||||
call);
|
call);
|
||||||
json_add_string(req->stream, "tx", hextx);
|
json_add_string(req->stream, "tx", hextx);
|
||||||
|
json_add_bool(req->stream, "allowhighfees", allowhighfees);
|
||||||
jsonrpc_request_end(req);
|
jsonrpc_request_end(req);
|
||||||
bitcoin_plugin_send(bitcoind, req);
|
bitcoin_plugin_send(bitcoind, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
|
||||||
|
const char *hextx,
|
||||||
|
void (*cb)(struct bitcoind *bitcoind,
|
||||||
|
bool success, const char *msg, void *),
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
return bitcoind_sendrawtx_ahf_(bitcoind, hextx, false, cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
/* `getrawblockbyheight`
|
/* `getrawblockbyheight`
|
||||||
*
|
*
|
||||||
* If no block were found at that height, will set each field to `null`.
|
* If no block were found at that height, will set each field to `null`.
|
||||||
|
|
|
@ -74,6 +74,21 @@ void bitcoind_estimate_fees_(struct bitcoind *bitcoind,
|
||||||
const u32 *), \
|
const u32 *), \
|
||||||
(arg))
|
(arg))
|
||||||
|
|
||||||
|
void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind,
|
||||||
|
const char *hextx,
|
||||||
|
bool allowhighfees,
|
||||||
|
void (*cb)(struct bitcoind *bitcoind,
|
||||||
|
bool success, const char *msg, void *),
|
||||||
|
void *arg);
|
||||||
|
#define bitcoind_sendrawtx_ahf(bitcoind_, hextx, allowhighfees, cb, arg)\
|
||||||
|
bitcoind_sendrawtx_ahf_((bitcoind_), (hextx), \
|
||||||
|
(allowhighfees), \
|
||||||
|
typesafe_cb_preargs(void, void *, \
|
||||||
|
(cb), (arg), \
|
||||||
|
struct bitcoind *, \
|
||||||
|
bool, const char *),\
|
||||||
|
(arg))
|
||||||
|
|
||||||
void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
|
void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
|
||||||
const char *hextx,
|
const char *hextx,
|
||||||
void (*cb)(struct bitcoind *bitcoind,
|
void (*cb)(struct bitcoind *bitcoind,
|
||||||
|
|
|
@ -218,10 +218,12 @@ static void broadcast_done(struct bitcoind *bitcoind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcast_tx(struct chain_topology *topo,
|
void broadcast_tx_ahf(struct chain_topology *topo,
|
||||||
struct channel *channel, const struct bitcoin_tx *tx,
|
struct channel *channel, const struct bitcoin_tx *tx,
|
||||||
void (*failed_or_success)(struct channel *channel,
|
bool allowhighfees,
|
||||||
bool success, const char *err))
|
void (*failed)(struct channel *channel,
|
||||||
|
bool success,
|
||||||
|
const char *err))
|
||||||
{
|
{
|
||||||
/* Channel might vanish: topo owns it to start with. */
|
/* Channel might vanish: topo owns it to start with. */
|
||||||
struct outgoing_tx *otx = tal(topo, struct outgoing_tx);
|
struct outgoing_tx *otx = tal(topo, struct outgoing_tx);
|
||||||
|
@ -230,7 +232,7 @@ void broadcast_tx(struct chain_topology *topo,
|
||||||
otx->channel = channel;
|
otx->channel = channel;
|
||||||
bitcoin_txid(tx, &otx->txid);
|
bitcoin_txid(tx, &otx->txid);
|
||||||
otx->hextx = tal_hex(otx, rawtx);
|
otx->hextx = tal_hex(otx, rawtx);
|
||||||
otx->failed_or_success = failed_or_success;
|
otx->failed_or_success = failed;
|
||||||
tal_free(rawtx);
|
tal_free(rawtx);
|
||||||
tal_add_destructor2(channel, clear_otx_channel, otx);
|
tal_add_destructor2(channel, clear_otx_channel, otx);
|
||||||
|
|
||||||
|
@ -238,8 +240,18 @@ void broadcast_tx(struct chain_topology *topo,
|
||||||
type_to_string(tmpctx, struct bitcoin_txid, &otx->txid));
|
type_to_string(tmpctx, struct bitcoin_txid, &otx->txid));
|
||||||
|
|
||||||
wallet_transaction_add(topo->ld->wallet, tx->wtx, 0, 0);
|
wallet_transaction_add(topo->ld->wallet, tx->wtx, 0, 0);
|
||||||
bitcoind_sendrawtx(topo->bitcoind, otx->hextx, broadcast_done, otx);
|
bitcoind_sendrawtx_ahf(topo->bitcoind, otx->hextx, allowhighfees,
|
||||||
|
broadcast_done, otx);
|
||||||
}
|
}
|
||||||
|
void broadcast_tx(struct chain_topology *topo,
|
||||||
|
struct channel *channel, const struct bitcoin_tx *tx,
|
||||||
|
void (*failed)(struct channel *channel,
|
||||||
|
bool success,
|
||||||
|
const char *err))
|
||||||
|
{
|
||||||
|
return broadcast_tx_ahf(topo, channel, tx, false, failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum watch_result closeinfo_txid_confirmed(struct lightningd *ld,
|
static enum watch_result closeinfo_txid_confirmed(struct lightningd *ld,
|
||||||
struct channel *channel,
|
struct channel *channel,
|
||||||
|
|
|
@ -182,6 +182,14 @@ void broadcast_tx(struct chain_topology *topo,
|
||||||
void (*failed)(struct channel *channel,
|
void (*failed)(struct channel *channel,
|
||||||
bool success,
|
bool success,
|
||||||
const char *err));
|
const char *err));
|
||||||
|
/* Like the above, but with an additional `allowhighfees` parameter.
|
||||||
|
* If true, suppress any high-fee checks in the backend. */
|
||||||
|
void broadcast_tx_ahf(struct chain_topology *topo,
|
||||||
|
struct channel *channel, const struct bitcoin_tx *tx,
|
||||||
|
bool allowhighfees,
|
||||||
|
void (*failed)(struct channel *channel,
|
||||||
|
bool success,
|
||||||
|
const char *err));
|
||||||
|
|
||||||
struct chain_topology *new_topology(struct lightningd *ld, struct log *log);
|
struct chain_topology *new_topology(struct lightningd *ld, struct log *log);
|
||||||
void setup_topology(struct chain_topology *topology, struct timers *timers,
|
void setup_topology(struct chain_topology *topology, struct timers *timers,
|
||||||
|
|
|
@ -282,8 +282,12 @@ static void handle_onchain_broadcast_tx(struct channel *channel,
|
||||||
wallet_transaction_annotate(w, &txid, type, channel->dbid);
|
wallet_transaction_annotate(w, &txid, type, channel->dbid);
|
||||||
|
|
||||||
/* We don't really care if it fails, we'll respond via watch. */
|
/* We don't really care if it fails, we'll respond via watch. */
|
||||||
broadcast_tx(channel->peer->ld->topology, channel, tx,
|
/* If the onchaind signals this as RBF-able, then we also
|
||||||
is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL);
|
* set allowhighfees, as the transaction may be RBFed into
|
||||||
|
* high feerates as protection against the MAD-HTLC attack. */
|
||||||
|
broadcast_tx_ahf(channel->peer->ld->topology, channel,
|
||||||
|
tx, is_rbf,
|
||||||
|
is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_onchain_unwatch_tx(struct channel *channel, const u8 *msg)
|
static void handle_onchain_unwatch_tx(struct channel *channel, const u8 *msg)
|
||||||
|
|
|
@ -753,13 +753,29 @@ static struct command_result *sendrawtransaction(struct command *cmd,
|
||||||
const jsmntok_t *toks)
|
const jsmntok_t *toks)
|
||||||
{
|
{
|
||||||
const char **params = tal_arr(cmd, const char *, 1);
|
const char **params = tal_arr(cmd, const char *, 1);
|
||||||
|
bool *allowhighfees;
|
||||||
|
|
||||||
/* bitcoin-cli wants strings. */
|
/* bitcoin-cli wants strings. */
|
||||||
if (!param(cmd, buf, toks,
|
if (!param(cmd, buf, toks,
|
||||||
p_req("tx", param_string, ¶ms[0]),
|
p_req("tx", param_string, ¶ms[0]),
|
||||||
|
p_req("allowhighfees", param_bool, &allowhighfees),
|
||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
return command_param_failed();
|
||||||
|
|
||||||
|
if (*allowhighfees) {
|
||||||
|
if (bitcoind->version >= 190001)
|
||||||
|
/* Starting in 19.0.1, second argument is
|
||||||
|
* maxfeerate, which when set to 0 means
|
||||||
|
* no max feerate.
|
||||||
|
*/
|
||||||
|
tal_arr_expand(¶ms, "0");
|
||||||
|
else
|
||||||
|
/* in older versions, second arg is allowhighfees,
|
||||||
|
* set to true to allow high fees.
|
||||||
|
*/
|
||||||
|
tal_arr_expand(¶ms, "true");
|
||||||
|
}
|
||||||
|
|
||||||
start_bitcoin_cli(NULL, cmd, process_sendrawtransaction, true,
|
start_bitcoin_cli(NULL, cmd, process_sendrawtransaction, true,
|
||||||
BITCOIND_HIGH_PRIO, "sendrawtransaction", params, NULL);
|
BITCOIND_HIGH_PRIO, "sendrawtransaction", params, NULL);
|
||||||
|
|
||||||
|
|
|
@ -1279,7 +1279,7 @@ def test_bcli(node_factory, bitcoind, chainparams):
|
||||||
"vout": fc['outnum']
|
"vout": fc['outnum']
|
||||||
})['amount'] is None)
|
})['amount'] is None)
|
||||||
|
|
||||||
resp = l1.rpc.call("sendrawtransaction", {"tx": "dummy"})
|
resp = l1.rpc.call("sendrawtransaction", {"tx": "dummy", "allowhighfees": False})
|
||||||
assert not resp["success"] and "decode failed" in resp["errmsg"]
|
assert not resp["success"] and "decode failed" in resp["errmsg"]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue