diff --git a/lightningd/build_utxos.c b/lightningd/build_utxos.c index 6576e2946..cdc14ca23 100644 --- a/lightningd/build_utxos.c +++ b/lightningd/build_utxos.c @@ -8,116 +8,6 @@ #include -/* FIXME: This is very slow with lots of inputs! */ -static bool can_spend(struct lightningd *ld, const u8 *script, - u32 *index, bool *output_is_p2sh) -{ - struct ext_key ext; - u64 bip32_max_index = db_get_intvar(ld->wallet->db, "bip32_max_index", 0); - u32 i; - - /* If not one of these, can't be for us. */ - if (is_p2sh(script)) - *output_is_p2sh = true; - else if (is_p2wpkh(script)) - *output_is_p2sh = false; - else - return false; - - for (i = 0; i < bip32_max_index; i++) { - u8 *s; - - if (bip32_key_from_parent(ld->bip32_base, i, - BIP32_FLAG_KEY_PUBLIC, &ext) - != WALLY_OK) { - abort(); - } - s = scriptpubkey_p2wpkh_derkey(ld, ext.pub_key); - if (*output_is_p2sh) { - u8 *p2sh = scriptpubkey_p2sh(ld, s); - tal_free(s); - s = p2sh; - } - if (scripteq(s, script)) { - tal_free(s); - *index = i; - return true; - } - tal_free(s); - } - return false; -} - -static void json_addfunds(struct command *cmd, - const char *buffer, const jsmntok_t *params) -{ - struct lightningd *ld = ld_from_dstate(cmd->dstate); - struct json_result *response = new_json_result(cmd); - jsmntok_t *txtok; - struct bitcoin_tx *tx; - int output; - size_t txhexlen, num_utxos = 0; - u64 total_satoshi = 0; - - if (!json_get_params(buffer, params, "tx", &txtok, NULL)) { - command_fail(cmd, "Need tx sending to address from newaddr"); - return; - } - - txhexlen = txtok->end - txtok->start; - tx = bitcoin_tx_from_hex(cmd, buffer + txtok->start, txhexlen); - if (!tx) { - command_fail(cmd, "'%.*s' is not a valid transaction", - txtok->end - txtok->start, - buffer + txtok->start); - return; - } - - /* Find an output we know how to spend. */ - for (output = 0; output < tal_count(tx->output); output++) { - struct utxo *utxo; - u32 index; - bool is_p2sh; - - if (!can_spend(ld, tx->output[output].script, &index, &is_p2sh)) - continue; - - utxo = tal(ld, struct utxo); - utxo->keyindex = index; - utxo->is_p2sh = is_p2sh; - utxo->amount = tx->output[output].amount; - utxo->status = output_state_available; - bitcoin_txid(tx, &utxo->txid); - utxo->outnum = output; - if (!wallet_add_utxo(ld->wallet, utxo, p2sh_wpkh)) { - command_fail(cmd, "Could add outputs to wallet"); - tal_free(utxo); - return; - } - total_satoshi += utxo->amount; - num_utxos++; - } - - if (!num_utxos) { - command_fail(cmd, "No usable outputs"); - return; - } - - json_object_start(response, NULL); - json_add_num(response, "outputs", num_utxos); - json_add_u64(response, "satoshis", total_satoshi); - json_object_end(response); - command_success(cmd, response); -} - -static const struct json_command addfunds_command = { - "addfunds", - json_addfunds, - "Add funds for lightningd to spend to create channels, using {tx}", - "Returns how many {outputs} it can use and total {satoshis}" -}; -AUTODATA(json_command, &addfunds_command); - const struct utxo **build_utxos(const tal_t *ctx, struct lightningd *ld, u64 satoshi_out, u32 feerate_per_kw, u64 dust_limit, diff --git a/wallet/Makefile b/wallet/Makefile index 6e936149e..b0b01e7e0 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -22,7 +22,7 @@ WALLET_TEST_PROGRAMS := $(WALLET_TEST_OBJS:.o=) $(WALLET_TEST_OBJS): $(WALLET_LIB_OBJS) -$(WALLET_TEST_PROGRAMS): $(CCAN_OBJS) daemon/log.o type_to_string.o daemon/pseudorand.o utils.o libsodium.a +$(WALLET_TEST_PROGRAMS): $(BITCOIN_OBJS) $(CCAN_OBJS) $(LIBBASE58_OBJS) daemon/log.o type_to_string.o daemon/pseudorand.o utils.o libwallycore.a libsecp256k1.a libsodium.a $(WALLET_TEST_OBJS): $(CCAN_HEADERS) wallet/tests: $(WALLET_TEST_PROGRAMS:%=unittest/%) diff --git a/wallet/wallet.c b/wallet/wallet.c index eb27b92b6..7f9e11dfe 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1,5 +1,6 @@ #include "wallet.h" +#include #include struct wallet *wallet_new(const tal_t *ctx, struct log *log) @@ -180,3 +181,43 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, } return utxos; } + +bool wallet_can_spend(struct wallet *w, const u8 *script, + u32 *index, bool *output_is_p2sh) +{ + struct ext_key ext; + u64 bip32_max_index = db_get_intvar(w->db, "bip32_max_index", 0); + u32 i; + + /* If not one of these, can't be for us. */ + if (is_p2sh(script)) + *output_is_p2sh = true; + else if (is_p2wpkh(script)) + *output_is_p2sh = false; + else + return false; + + for (i = 0; i < bip32_max_index; i++) { + u8 *s; + + if (bip32_key_from_parent(w->bip32_base, i, + BIP32_FLAG_KEY_PUBLIC, &ext) + != WALLY_OK) { + abort(); + } + s = scriptpubkey_p2wpkh_derkey(w, ext.pub_key); + if (*output_is_p2sh) { + u8 *p2sh = scriptpubkey_p2sh(w, s); + tal_free(s); + s = p2sh; + } + if (scripteq(s, script)) { + tal_free(s); + *index = i; + return true; + } + tal_free(s); + } + return false; +} + diff --git a/wallet/wallet.h b/wallet/wallet.h index 450ce237b..f3f3064f0 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -91,4 +91,17 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, */ void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos); +/** + * wallet_can_spend - Do we have the private key matching this scriptpubkey? + * + * FIXME: This is very slow with lots of inputs! + * + * @w: (in) allet holding the pubkeys to check against (privkeys are on HSM) + * @script: (in) the script to check + * @index: (out) the bip32 derivation index that matched the script + * @output_is_p2sh: (out) whether the script is a p2sh, or p2wpkh + */ +bool wallet_can_spend(struct wallet *w, const u8 *script, + u32 *index, bool *output_is_p2sh); + #endif /* WALLET_WALLET_H */ diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 0ccbf159b..075abf7f1 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -237,3 +237,73 @@ static const struct json_command newaddr_command = { "Returns {address} a p2sh address" }; AUTODATA(json_command, &newaddr_command); + +static void json_addfunds(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + struct lightningd *ld = ld_from_dstate(cmd->dstate); + struct json_result *response = new_json_result(cmd); + jsmntok_t *txtok; + struct bitcoin_tx *tx; + int output; + size_t txhexlen, num_utxos = 0; + u64 total_satoshi = 0; + + if (!json_get_params(buffer, params, "tx", &txtok, NULL)) { + command_fail(cmd, "Need tx sending to address from newaddr"); + return; + } + + txhexlen = txtok->end - txtok->start; + tx = bitcoin_tx_from_hex(cmd, buffer + txtok->start, txhexlen); + if (!tx) { + command_fail(cmd, "'%.*s' is not a valid transaction", + txtok->end - txtok->start, + buffer + txtok->start); + return; + } + + /* Find an output we know how to spend. */ + for (output = 0; output < tal_count(tx->output); output++) { + struct utxo *utxo; + u32 index; + bool is_p2sh; + + if (!wallet_can_spend(ld->wallet, tx->output[output].script, &index, &is_p2sh)) + continue; + + utxo = tal(ld, struct utxo); + utxo->keyindex = index; + utxo->is_p2sh = is_p2sh; + utxo->amount = tx->output[output].amount; + utxo->status = output_state_available; + bitcoin_txid(tx, &utxo->txid); + utxo->outnum = output; + if (!wallet_add_utxo(ld->wallet, utxo, p2sh_wpkh)) { + command_fail(cmd, "Could add outputs to wallet"); + tal_free(utxo); + return; + } + total_satoshi += utxo->amount; + num_utxos++; + } + + if (!num_utxos) { + command_fail(cmd, "No usable outputs"); + return; + } + + json_object_start(response, NULL); + json_add_num(response, "outputs", num_utxos); + json_add_u64(response, "satoshis", total_satoshi); + json_object_end(response); + command_success(cmd, response); +} + +static const struct json_command addfunds_command = { + "addfunds", + json_addfunds, + "Add funds for lightningd to spend to create channels, using {tx}", + "Returns how many {outputs} it can use and total {satoshis}" +}; +AUTODATA(json_command, &addfunds_command);