Skip to content

Commit ae88556

Browse files
committed
plugins/bcli: use -stdin to feed arguments, in case we have a giant tx.
``` lightningd-1 2025-10-27T11:26:04.285Z **BROKEN** plugin-bcli: bitcoin-cli exec failed: Argument list too long ``` Use -stdin to bitcoin-cli: we can then handle arguments of arbitrary length. Fixes: #8634 Changelog-Fixed: plugins: `bcli` would fail with "Argument list too long" when sending a giant tx.
1 parent 54a187c commit ae88556

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

plugins/bcli.c

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct bitcoin_cli {
7676
int *exitstatus;
7777
pid_t pid;
7878
const char **args;
79+
const char **stdinargs;
7980
struct timeabs start;
8081
enum bitcoind_prio prio;
8182
char *output;
@@ -95,7 +96,8 @@ static void add_arg(const char ***args, const char *arg TAKES)
9596
tal_arr_expand(args, arg);
9697
}
9798

98-
static const char **gather_argsv(const tal_t *ctx, const char *cmd, va_list ap)
99+
/* If stdinargs is non-NULL, that is where we put additional args */
100+
static const char **gather_argsv(const tal_t *ctx, const char ***stdinargs, const char *cmd, va_list ap)
99101
{
100102
const char **args = tal_arr(ctx, const char *, 1);
101103
const char *arg;
@@ -128,23 +130,30 @@ static const char **gather_argsv(const tal_t *ctx, const char *cmd, va_list ap)
128130
// `-rpcpassword` argument - secrets in arguments can leak when listing
129131
// system processes.
130132
add_arg(&args, "-stdinrpcpass");
133+
/* To avoid giant command lines, we use -stdin (avail since bitcoin 0.13) */
134+
if (stdinargs)
135+
add_arg(&args, "-stdin");
131136

132137
add_arg(&args, cmd);
133-
while ((arg = va_arg(ap, char *)) != NULL)
134-
add_arg(&args, arg);
138+
while ((arg = va_arg(ap, char *)) != NULL) {
139+
if (stdinargs)
140+
add_arg(stdinargs, arg);
141+
else
142+
add_arg(&args, arg);
143+
}
135144
add_arg(&args, NULL);
136145

137146
return args;
138147
}
139148

140149
static LAST_ARG_NULL const char **
141-
gather_args(const tal_t *ctx, const char *cmd, ...)
150+
gather_args(const tal_t *ctx, const char ***stdinargs, const char *cmd, ...)
142151
{
143152
va_list ap;
144153
const char **ret;
145154

146155
va_start(ap, cmd);
147-
ret = gather_argsv(ctx, cmd, ap);
156+
ret = gather_argsv(ctx, stdinargs, cmd, ap);
148157
va_end(ap);
149158

150159
return ret;
@@ -170,7 +179,7 @@ static struct io_plan *output_init(struct io_conn *conn, struct bitcoin_cli *bcl
170179
static void next_bcli(enum bitcoind_prio prio);
171180

172181
/* For printing: simple string of args (no secrets!) */
173-
static char *args_string(const tal_t *ctx, const char **args)
182+
static char *args_string(const tal_t *ctx, const char **args, const char **stdinargs)
174183
{
175184
size_t i;
176185
char *ret = tal_strdup(ctx, args[0]);
@@ -185,12 +194,16 @@ static char *args_string(const tal_t *ctx, const char **args)
185194
ret = tal_strcat(ctx, take(ret), args[i]);
186195
}
187196
}
197+
for (i = 0; i < tal_count(stdinargs); i++) {
198+
ret = tal_strcat(ctx, take(ret), " ");
199+
ret = tal_strcat(ctx, take(ret), stdinargs[i]);
200+
}
188201
return ret;
189202
}
190203

191204
static char *bcli_args(const tal_t *ctx, struct bitcoin_cli *bcli)
192205
{
193-
return args_string(ctx, bcli->args);
206+
return args_string(ctx, bcli->args, bcli->stdinargs);
194207
}
195208

196209
/* Only set as destructor once bcli is in current. */
@@ -315,7 +328,10 @@ static void next_bcli(enum bitcoind_prio prio)
315328

316329
if (bitcoind->rpcpass)
317330
write_all(in, bitcoind->rpcpass, strlen(bitcoind->rpcpass));
318-
331+
for (size_t i = 0; i < tal_count(bcli->stdinargs); i++) {
332+
write_all(in, "\n", strlen("\n"));
333+
write_all(in, bcli->stdinargs[i], strlen(bcli->stdinargs[i]));
334+
}
319335
close(in);
320336

321337
bcli->start = time_now();
@@ -351,7 +367,8 @@ start_bitcoin_cliv(const tal_t *ctx,
351367
else
352368
bcli->exitstatus = NULL;
353369

354-
bcli->args = gather_argsv(bcli, method, ap);
370+
bcli->stdinargs = tal_arr(bcli, const char *, 0);
371+
bcli->args = gather_argsv(bcli, &bcli->stdinargs, method, ap);
355372
bcli->stash = stash;
356373

357374
list_add_tail(&bitcoind->pending[bcli->prio], &bcli->list);
@@ -994,14 +1011,14 @@ static struct command_result *getutxout(struct command *cmd,
9941011

9951012
static void bitcoind_failure(struct plugin *p, const char *error_message)
9961013
{
997-
const char **cmd = gather_args(bitcoind, "echo", NULL);
1014+
const char **cmd = gather_args(bitcoind, NULL, "echo", NULL);
9981015
plugin_err(p, "\n%s\n\n"
9991016
"Make sure you have bitcoind running and that bitcoin-cli"
10001017
" is able to connect to bitcoind.\n\n"
10011018
"You can verify that your Bitcoin Core installation is"
10021019
" ready for use by running:\n\n"
10031020
" $ %s 'hello world'\n", error_message,
1004-
args_string(cmd, cmd));
1021+
args_string(cmd, cmd, NULL));
10051022
}
10061023

10071024
/* Do some sanity checks on bitcoind based on the output of `getnetworkinfo`. */
@@ -1016,7 +1033,7 @@ static void parse_getnetworkinfo_result(struct plugin *p, const char *buf)
10161033
if (!result)
10171034
plugin_err(p, "Invalid response to '%s': '%s'. Can not "
10181035
"continue without proceeding to sanity checks.",
1019-
args_string(tmpctx, gather_args(bitcoind, "getnetworkinfo", NULL)),
1036+
args_string(tmpctx, gather_args(bitcoind, NULL, "getnetworkinfo", NULL), NULL),
10201037
buf);
10211038

10221039
/* Check that we have a fully-featured `estimatesmartfee`. */
@@ -1047,7 +1064,7 @@ static void wait_and_check_bitcoind(struct plugin *p)
10471064
int in, from, status;
10481065
pid_t child;
10491066
const char **cmd = gather_args(
1050-
bitcoind, "-rpcwait", "-rpcwaittimeout=30", "getnetworkinfo", NULL);
1067+
bitcoind, NULL, "-rpcwait", "-rpcwaittimeout=30", "getnetworkinfo", NULL);
10511068
char *output = NULL;
10521069

10531070
child = pipecmdarr(&in, &from, &from, cast_const2(char **, cmd));

tests/test_plugin.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1875,7 +1875,6 @@ def test_bitcoin_backend(node_factory, bitcoind):
18751875
" bitcoind")
18761876

18771877

1878-
@pytest.mark.xfail(strict=True)
18791878
def test_bitcoin_backend_gianttx(node_factory, bitcoind):
18801879
"""Test that a giant tx doesn't crash bcli"""
18811880
l1 = node_factory.get_node(start=False)

0 commit comments

Comments
 (0)