bitcoind: use fundrawtransaction rather than sendtoaddress and -nowalletbroadcast

Luke-Jr points out this is the Right Way to do these things.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2016-04-11 16:40:53 +09:30
parent 587cda0ab8
commit 95340aa03f
4 changed files with 159 additions and 58 deletions

View file

@ -390,41 +390,147 @@ void bitcoind_send_tx(struct lightningd_state *dstate,
tal_free(raw); tal_free(raw);
} }
static void process_sendtoaddress(struct bitcoin_cli *bcli) struct funding_process {
struct peer *peer;
void (*cb)(struct lightningd_state *state,
const struct bitcoin_tx *tx,
int change_output,
struct peer *peer);
int change_output;
};
static void process_signrawtransaction(struct bitcoin_cli *bcli)
{ {
const char *out = (char *)bcli->output; const jsmntok_t *tokens, *hex, *complete;
struct txid_lookup *lookup = tal(bcli->dstate, struct txid_lookup); bool valid;
struct bitcoin_tx *tx;
struct funding_process *f = bcli->cb_arg;
/* We expect a txid (followed by \n, vs buffer including \0) */ /* Output:
if (bcli->output_bytes != sizeof(lookup->txidhex)) "{\n"
fatal("sendtoaddress failed: %.*s", " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
(int)bcli->output_bytes, out); " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
...*/
if (!bcli->output)
fatal("%s signrawtransaction %s: failed",
bcli->args[0], bcli->args[3]);
memcpy(lookup->txidhex, bcli->output, sizeof(lookup->txidhex)-1); tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
lookup->txidhex[sizeof(lookup->txidhex)-1] = '\0'; if (!tokens)
log_debug(bcli->dstate->base_log, "sendtoaddress gave %s", fatal("%s signrawtransaction %s: %s response (%.*s)?",
lookup->txidhex); bcli->args[0], bcli->args[2],
lookup->cb_arg = bcli->cb_arg; valid ? "partial" : "invalid",
(int)bcli->output_bytes, bcli->output);
if (tokens[0].type != JSMN_OBJECT)
fatal("%s signrawtransaction %s: gave non-object (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
/* Now we need the actual transaction. */ complete = json_get_member(bcli->output, tokens, "complete");
start_bitcoin_cli(bcli->dstate, process_tx, bcli->cb, lookup, if (!complete)
"gettransaction", lookup->txidhex, NULL); fatal("%s signrawtransaction %s had no complete member (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
if (complete->end - complete->start != strlen("true")
|| strncmp(bcli->output + complete->start, "true", strlen("true")))
fatal("%s signrawtransaction %s not complete (%.*s)?",
bcli->args[0], bcli->args[2],
complete->end - complete->start,
bcli->output + complete->start);
hex = json_get_member(bcli->output, tokens, "hex");
if (!hex)
fatal("%s signrawtransaction %s had no hex member (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
tx = bitcoin_tx_from_hex(bcli, bcli->output + hex->start,
hex->end - hex->start);
if (!tx)
fatal("%s signrawtransaction %s had bad hex member (%.*s)?",
bcli->args[0], bcli->args[2],
hex->end - hex->start, bcli->output + hex->start);
f->cb(bcli->dstate, tx, f->change_output, f->peer);
} }
void bitcoind_create_payment(struct lightningd_state *dstate, /* FIXME: handle lack of funds! */
const char *addr, static void process_fundrawtransaction(struct bitcoin_cli *bcli)
u64 satoshis, {
const jsmntok_t *tokens, *hex, *changepos;
char *hexstr, *end;
bool valid;
struct funding_process *f = bcli->cb_arg;
/* Output:
"{\n"
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
" \"fee\": n, (numeric) Fee the resulting transaction pays\n"
" \"changepos\": n (numeric) The position of the added change output, or -1\n"
"}\n"
*/
if (!bcli->output)
fatal("%s fundrawtransaction %s: failed",
bcli->args[0], bcli->args[3]);
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
if (!tokens)
fatal("%s fundrawtransaction %s: %s response (%.*s)?",
bcli->args[0], bcli->args[2],
valid ? "partial" : "invalid",
(int)bcli->output_bytes, bcli->output);
if (tokens[0].type != JSMN_OBJECT)
fatal("%s fundrawtransaction %s: gave non-object (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
hex = json_get_member(bcli->output, tokens, "hex");
if (!hex)
fatal("%s fundrawtransaction %s had no hex member (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
changepos = json_get_member(bcli->output, tokens, "changepos");
if (!changepos)
fatal("%s fundrawtransaction %s had no changepos member (%.*s)?",
bcli->args[0], bcli->args[2],
(int)bcli->output_bytes, bcli->output);
f->change_output = strtol(bcli->output + changepos->start, &end, 0);
if (end != bcli->output + changepos->end)
fatal("%s fundrawtransaction %s had bad changepos (%.*s)?",
bcli->args[0], bcli->args[2],
changepos->end - changepos->start,
bcli->output + changepos->start);
/* We need a nul-terminated string. */
hexstr = tal_strndup(bcli, bcli->output + hex->start,
hex->end - hex->start);
/* Now we need to sign those inputs! */
start_bitcoin_cli(bcli->dstate, process_signrawtransaction, NULL,
f, "signrawtransaction", hexstr, NULL);
}
/* Adds and signs inputs to this tx from wallet. */
void bitcoind_fund_transaction(struct lightningd_state *dstate,
struct bitcoin_tx *tx_no_inputs,
void (*cb)(struct lightningd_state *dstate, void (*cb)(struct lightningd_state *dstate,
const struct bitcoin_tx *tx, const struct bitcoin_tx *tx,
int change_output,
struct peer *peer), struct peer *peer),
struct peer *peer) struct peer *peer)
{ {
char amtstr[STR_MAX_CHARS(satoshis) * 2 + 1]; struct funding_process *f = tal(peer, struct funding_process);
sprintf(amtstr, "%"PRIu64 "." "%08"PRIu64, u8 *raw = linearize_tx(dstate, tx_no_inputs);
satoshis / 100000000, satoshis % 100000000); char *hex = tal_arr(raw, char, hex_str_size(tal_count(raw)));
start_bitcoin_cli(dstate, process_sendtoaddress, cb, peer, assert(tx_no_inputs->input_count == 0);
"sendtoaddress", addr, amtstr, NULL);
hex_encode(raw, tal_count(raw), hex, tal_count(hex));
f->peer = peer;
f->cb = cb;
start_bitcoin_cli(dstate, process_fundrawtransaction, NULL, f,
"fundrawtransaction", hex, NULL);
tal_free(raw);
} }
static void process_getblock(struct bitcoin_cli *bcli) static void process_getblock(struct bitcoin_cli *bcli)
@ -474,14 +580,12 @@ void bitcoind_get_mediantime(struct lightningd_state *dstate,
"getblock", hex, NULL); "getblock", hex, NULL);
} }
/* We make sure they have walletbroadcast=0, so we don't broadcast /* Make testnet/regtest status matches us. */
* the anchor. */
void check_bitcoind_config(struct lightningd_state *dstate) void check_bitcoind_config(struct lightningd_state *dstate)
{ {
void *ctx = tal(dstate, char); void *ctx = tal(dstate, char);
char *path, *config, **lines; char *path, *config, **lines;
size_t i; size_t i;
bool nowalletbroadcast = false;
int testnet = -1, regtest = -1; int testnet = -1, regtest = -1;
path = path_simplify(ctx, path_join(ctx, path_cwd(ctx), path = path_simplify(ctx, path_join(ctx, path_cwd(ctx),
@ -497,9 +601,6 @@ void check_bitcoind_config(struct lightningd_state *dstate)
for (i = 0; lines[i]; i++) { for (i = 0; lines[i]; i++) {
char *str; char *str;
if (tal_strreg(ctx, lines[i], if (tal_strreg(ctx, lines[i],
"^[ \t]*walletbroadcast[ \t]*=[ \t]*0"))
nowalletbroadcast = true;
else if (tal_strreg(ctx, lines[i],
"^[ \t]*testnet[ \t]*=[ \t]*([01])", &str)) "^[ \t]*testnet[ \t]*=[ \t]*([01])", &str))
testnet = atoi(str); testnet = atoi(str);
else if (tal_strreg(ctx, lines[i], else if (tal_strreg(ctx, lines[i],
@ -507,10 +608,6 @@ void check_bitcoind_config(struct lightningd_state *dstate)
regtest = atoi(str); regtest = atoi(str);
} }
if (!nowalletbroadcast)
log_unusual(dstate->base_log,
"%s does not contain walletbroadcast=0",
path);
if (dstate->config.testnet) { if (dstate->config.testnet) {
if (testnet != 1 && regtest != 1) if (testnet != 1 && regtest != 1)
log_unusual(dstate->base_log, log_unusual(dstate->base_log,

View file

@ -40,11 +40,11 @@ void bitcoind_txid_lookup_(struct lightningd_state *dstate,
void bitcoind_send_tx(struct lightningd_state *dstate, void bitcoind_send_tx(struct lightningd_state *dstate,
const struct bitcoin_tx *tx); const struct bitcoin_tx *tx);
void bitcoind_create_payment(struct lightningd_state *dstate, void bitcoind_fund_transaction(struct lightningd_state *dstate,
const char *addr, struct bitcoin_tx *tx_no_inputs,
u64 satoshis,
void (*cb)(struct lightningd_state *dstate, void (*cb)(struct lightningd_state *dstate,
const struct bitcoin_tx *tx, const struct bitcoin_tx *tx,
int change_output,
struct peer *peer), struct peer *peer),
struct peer *peer); struct peer *peer);

View file

@ -1221,10 +1221,19 @@ const struct bitcoin_tx *bitcoin_htlc_spend(const struct peer *peer,
static void created_anchor(struct lightningd_state *dstate, static void created_anchor(struct lightningd_state *dstate,
const struct bitcoin_tx *tx, const struct bitcoin_tx *tx,
int change_output,
struct peer *peer) struct peer *peer)
{ {
size_t real_out;
bitcoin_txid(tx, &peer->anchor.txid); bitcoin_txid(tx, &peer->anchor.txid);
peer->anchor.index = find_p2sh_out(tx, peer->anchor.redeemscript); if (change_output == -1)
real_out = 0;
else
real_out = !change_output;
assert(find_p2sh_out(tx, peer->anchor.redeemscript) == real_out);
peer->anchor.index = real_out;
assert(peer->anchor.satoshis == tx->output[peer->anchor.index].amount); assert(peer->anchor.satoshis == tx->output[peer->anchor.index].amount);
/* We'll need this later, when we're told to broadcast it. */ /* We'll need this later, when we're told to broadcast it. */
peer->anchor.tx = tal_steal(peer, tx); peer->anchor.tx = tal_steal(peer, tx);
@ -1235,24 +1244,20 @@ static void created_anchor(struct lightningd_state *dstate,
/* Start creation of the bitcoin anchor tx. */ /* Start creation of the bitcoin anchor tx. */
void bitcoin_create_anchor(struct peer *peer, enum state_input done) void bitcoin_create_anchor(struct peer *peer, enum state_input done)
{ {
struct sha256 h; struct bitcoin_tx *template = bitcoin_tx(peer, 0, 1);
struct ripemd160 redeemhash;
char *p2shaddr;
/* We must be offering anchor for us to try creating it */ /* We must be offering anchor for us to try creating it */
assert(peer->us.offer_anchor); assert(peer->us.offer_anchor);
sha256(&h, peer->anchor.redeemscript, template->output[0].amount = peer->anchor.satoshis;
tal_count(peer->anchor.redeemscript)); template->output[0].script
ripemd160(&redeemhash, h.u.u8, sizeof(h)); = scriptpubkey_p2sh(template, peer->anchor.redeemscript);
template->output[0].script_length
p2shaddr = p2sh_to_base58(peer, peer->dstate->config.testnet, = tal_count(template->output[0].script);
&redeemhash);
assert(done == BITCOIN_ANCHOR_CREATED); assert(done == BITCOIN_ANCHOR_CREATED);
bitcoind_create_payment(peer->dstate, p2shaddr, peer->anchor.satoshis, bitcoind_fund_transaction(peer->dstate, template, created_anchor, peer);
created_anchor, peer);
} }
/* We didn't end up broadcasting the anchor: release the utxos. /* We didn't end up broadcasting the anchor: release the utxos.

View file

@ -15,7 +15,6 @@ mkdir $DATADIR
cat > $DATADIR/bitcoin.conf <<EOF cat > $DATADIR/bitcoin.conf <<EOF
regtest=1 regtest=1
testnet=0 testnet=0
walletbroadcast=0
EOF EOF
$DAEMON & $DAEMON &