bitcoind: getoutput so we can check short_channel_ids.

It would be nice if bitcoind had an RPC to do this in one, but that's
a bit much to ask for.  We could also hand around proofs, for lite nodes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2018-01-04 22:10:58 +10:30 committed by Christian Decker
parent 61be62ddb7
commit 2b1eb6a677
2 changed files with 185 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include <common/utils.h>
#include <errno.h>
#include <inttypes.h>
#include <lightningd/chaintopology.h>
#define BITCOIN_CLI "bitcoin-cli"
@ -413,6 +414,172 @@ void bitcoind_getblockcount_(struct bitcoind *bitcoind,
"getblockcount", NULL);
}
struct get_output {
unsigned int blocknum, txnum, outnum;
/* The real callback arg */
void *cbarg;
};
static void process_gettxout(struct bitcoin_cli *bcli)
{
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg) = bcli->cb;
const struct get_output *go = bcli->cb_arg;
void *cbarg = go->cbarg;
const jsmntok_t *tokens, *valuetok, *scriptpubkeytok, *hextok;
struct bitcoin_tx_output out;
bool valid;
if (*bcli->exitstatus != 0) {
log_debug(bcli->bitcoind->log, "%s: not unspent output?",
bcli_args(bcli));
tal_free(go);
cb(bcli->bitcoind, NULL, cbarg);
return;
}
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
if (!tokens)
fatal("%s: %s response",
bcli_args(bcli), valid ? "partial" : "invalid");
if (tokens[0].type != JSMN_OBJECT)
fatal("%s: gave non-object (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
valuetok = json_get_member(bcli->output, tokens, "value");
if (!valuetok)
fatal("%s: had no value member (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
if (!json_tok_bitcoin_amount(bcli->output, valuetok, &out.amount))
fatal("%s: had bad value (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
scriptpubkeytok = json_get_member(bcli->output, tokens, "scriptPubKey");
if (!scriptpubkeytok)
fatal("%s: had no scriptPubKey member (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
hextok = json_get_member(bcli->output, scriptpubkeytok, "hex");
if (!hextok)
fatal("%s: had no scriptPubKey->hex member (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
out.script = tal_hexdata(bcli, bcli->output + hextok->start,
hextok->end - hextok->start);
if (!out.script)
fatal("%s: scriptPubKey->hex invalid hex (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
tal_free(go);
cb(bcli->bitcoind, &out, cbarg);
}
static void process_getblock(struct bitcoin_cli *bcli)
{
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg) = bcli->cb;
struct get_output *go = bcli->cb_arg;
const jsmntok_t *tokens, *txstok, *txidtok;
struct bitcoin_txid txid;
bool valid;
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
if (!tokens)
fatal("%s: %s response",
bcli_args(bcli), valid ? "partial" : "invalid");
if (tokens[0].type != JSMN_OBJECT)
fatal("%s: gave non-object (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
/* "tx": [
"1a7bb0f58a5d235d232deb61d9e2208dabe69848883677abe78e9291a00638e8",
"56a7e3468c16a4e21a4722370b41f522ad9dd8006c0e4e73c7d1c47f80eced94",
...
*/
txstok = json_get_member(bcli->output, tokens, "tx");
if (!txstok)
fatal("%s: had no tx member (%.*s)?",
bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
/* Now, this can certainly happen, if txnum too large. */
txidtok = json_get_arr(txstok, go->txnum);
if (!txidtok) {
void *cbarg = go->cbarg;
log_debug(bcli->bitcoind->log, "%s: no txnum %u",
bcli_args(bcli), go->txnum);
tal_free(go);
cb(bcli->bitcoind, NULL, cbarg);
return;
}
if (!bitcoin_txid_from_hex(bcli->output + txidtok->start,
txidtok->end - txidtok->start,
&txid))
fatal("%s: had bad txid (%.*s)?",
bcli_args(bcli),
txidtok->end - txidtok->start,
bcli->output + txidtok->start);
/* Now get the raw tx output. */
start_bitcoin_cli(bcli->bitcoind, NULL,
process_gettxout, true, cb, go,
"gettxout",
take(type_to_string(go, struct bitcoin_txid, &txid)),
take(tal_fmt(go, "%u", go->outnum)),
NULL);
}
static void process_getblockhash_for_txout(struct bitcoin_cli *bcli)
{
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg) = bcli->cb;
struct get_output *go = bcli->cb_arg;
if (*bcli->exitstatus != 0) {
void *cbarg = go->cbarg;
log_debug(bcli->bitcoind->log, "%s: invalid blocknum?",
bcli_args(bcli));
tal_free(go);
cb(bcli->bitcoind, NULL, cbarg);
return;
}
start_bitcoin_cli(bcli->bitcoind, NULL, process_getblock, false, cb, go,
"getblock",
take(tal_strndup(go, bcli->output,bcli->output_bytes)),
NULL);
}
void bitcoind_getoutput_(struct bitcoind *bitcoind,
unsigned int blocknum, unsigned int txnum,
unsigned int outnum,
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg),
void *arg)
{
struct get_output *go = tal(bitcoind, struct get_output);
go->blocknum = blocknum;
go->txnum = txnum;
go->outnum = outnum;
go->cbarg = arg;
/* We may not have topology ourselves that far back, so ask bitcoind */
start_bitcoin_cli(bitcoind, NULL, process_getblockhash_for_txout,
true, cb, go,
"getblockhash", take(tal_fmt(go, "%u", blocknum)),
NULL);
/* Looks like a leak, but we free it in process_getblock */
notleak(go);
}
static void process_getblockhash(struct bitcoin_cli *bcli)
{
struct bitcoin_blkid blkid;

View File

@ -10,6 +10,8 @@
#include <stdbool.h>
struct bitcoin_blkid;
struct bitcoin_tx_output;
struct block;
struct lightningd;
struct ripemd160;
struct bitcoin_tx;
@ -127,4 +129,20 @@ void bitcoind_getrawblock_(struct bitcoind *bitcoind,
struct bitcoind *, \
struct bitcoin_block *), \
(arg))
void bitcoind_getoutput_(struct bitcoind *bitcoind,
unsigned int blocknum, unsigned int txnum,
unsigned int outnum,
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg),
void *arg);
#define bitcoind_getoutput(bitcoind_, blocknum, txnum, outnum, cb, arg) \
bitcoind_getoutput_((bitcoind_), (blocknum), (txnum), (outnum), \
typesafe_cb_preargs(void, void *, \
(cb), (arg), \
struct bitcoind *, \
const struct bitcoin_tx_output*), \
(arg))
#endif /* LIGHTNING_LIGHTNINGD_BITCOIND_H */