diff --git a/daemon/Makefile b/daemon/Makefile index 267ba6bee..1e0554823 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -25,6 +25,7 @@ DAEMON_SRC := \ daemon/packets.c \ daemon/secrets.c \ daemon/timeout.c \ + daemon/wallet.c \ daemon/watch.c \ names.c \ state.c @@ -52,6 +53,7 @@ DAEMON_HEADERS := \ daemon/pseudorand.h \ daemon/secrets.h \ daemon/timeout.h \ + daemon/wallet.h \ daemon/watch.h $(DAEMON_OBJS) $(DAEMON_LIB_OBJS) $(DAEMON_CLI_OBJS): $(DAEMON_HEADERS) $(DAEMON_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) diff --git a/daemon/jsonrpc.c b/daemon/jsonrpc.c index b6184eeaa..b4ff96545 100644 --- a/daemon/jsonrpc.c +++ b/daemon/jsonrpc.c @@ -254,6 +254,7 @@ static const struct json_command *cmdlist[] = { &failhtlc_command, &commit_command, &close_command, + &newaddr_command, /* Developer/debugging options. */ &echo_command, &rhash_command, diff --git a/daemon/jsonrpc.h b/daemon/jsonrpc.h index f65e672e0..84a73d923 100644 --- a/daemon/jsonrpc.h +++ b/daemon/jsonrpc.h @@ -64,4 +64,5 @@ extern const struct json_command failhtlc_command; extern const struct json_command commit_command; extern const struct json_command mocktime_command; extern const struct json_command close_command; +extern const struct json_command newaddr_command; #endif /* LIGHTNING_DAEMON_JSONRPC_H */ diff --git a/daemon/lightningd.c b/daemon/lightningd.c index 679848935..b7629f95b 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -183,6 +183,7 @@ static struct lightningd_state *lightningd_state(void) | SECP256K1_CONTEXT_SIGN); default_config(&dstate->config); list_head_init(&dstate->bitcoin_req); + list_head_init(&dstate->wallet); dstate->bitcoin_req_running = false; return dstate; } diff --git a/daemon/lightningd.h b/daemon/lightningd.h index b1494ce06..9ea995a9f 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -81,5 +81,8 @@ struct lightningd_state { /* Outstanding bitcoind requests. */ struct list_head bitcoin_req; bool bitcoin_req_running; + + /* Wallet addresses we maintain. */ + struct list_head wallet; }; #endif /* LIGHTNING_DAEMON_LIGHTNING_H */ diff --git a/daemon/wallet.c b/daemon/wallet.c new file mode 100644 index 000000000..d0420bf52 --- /dev/null +++ b/daemon/wallet.c @@ -0,0 +1,107 @@ +/* Poor man's wallet. + * Needed because bitcoind doesn't (yet) produce segwit outputs, and we need + * such outputs for our anchor tx to make it immalleable. + */ +#include "bitcoin/base58.h" +#include "bitcoin/privkey.h" +#include "bitcoin/script.h" +#include "bitcoin/signature.h" +#include "bitcoin/tx.h" +#include "jsonrpc.h" +#include "lightningd.h" +#include "log.h" +#include "wallet.h" +#include +#include + +struct wallet { + struct list_node list; + struct privkey privkey; + struct pubkey pubkey; + struct ripemd160 p2sh; +}; + +static void new_keypair(struct lightningd_state *dstate, + struct privkey *privkey, struct pubkey *pubkey) +{ + do { + if (RAND_bytes(privkey->secret, sizeof(privkey->secret)) != 1) + fatal("Could not get random bytes for privkey"); + } while (!pubkey_from_privkey(dstate->secpctx, + privkey, pubkey, SECP256K1_EC_COMPRESSED)); +} + +void wallet_add_signed_input(struct lightningd_state *dstate, + const struct wallet *w, + struct bitcoin_tx *tx, + unsigned int input_num) +{ + u8 *redeemscript; + struct bitcoin_signature sig; + assert(input_num < tx->input_count); + + redeemscript = bitcoin_redeem_single(tx, &w->pubkey); + + sig.stype = SIGHASH_ALL; + sign_tx_input(dstate->secpctx, tx, input_num, + redeemscript, tal_count(redeemscript), + NULL, + &w->privkey, + &w->pubkey, + &sig.sig); + + tx->input[input_num].script + = scriptsig_p2sh_single_sig(tx->input, + redeemscript, + tal_count(redeemscript), + &sig); + tx->input[input_num].script_length + = tal_count(tx->input[input_num].script); +} + +struct wallet *wallet_can_spend(struct lightningd_state *dstate, + const struct bitcoin_tx_output *output) +{ + struct ripemd160 h; + struct wallet *w; + + if (!is_p2sh(output->script, output->script_length)) + return NULL; + + memcpy(&h, output->script + 2, 20); + list_for_each(&dstate->wallet, w, list) { + if (structeq(&h, &w->p2sh)) + return w; + } + return NULL; +} + +static void json_newaddr(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + struct json_result *response = new_json_result(cmd); + struct wallet *w = tal(cmd->dstate, struct wallet); + u8 *redeemscript; + struct sha256 h; + + new_keypair(cmd->dstate, &w->privkey, &w->pubkey); + redeemscript = bitcoin_redeem_single(cmd, &w->pubkey); + sha256(&h, redeemscript, tal_count(redeemscript)); + ripemd160(&w->p2sh, h.u.u8, sizeof(h)); + + list_add_tail(&cmd->dstate->wallet, &w->list); + + json_object_start(response, NULL); + json_add_string(response, "address", + p2sh_to_base58(cmd, cmd->dstate->config.testnet, + &w->p2sh)); + json_object_end(response); + command_success(cmd, response); +} + +const struct json_command newaddr_command = { + "newaddr", + json_newaddr, + "Get a new address to fund a channel", + "Returns {address} a p2sh address" +}; diff --git a/daemon/wallet.h b/daemon/wallet.h new file mode 100644 index 000000000..ffa8509f7 --- /dev/null +++ b/daemon/wallet.h @@ -0,0 +1,18 @@ +#ifndef LIGHTNING_DAEMON_WALLET_H +#define LIGHTNING_DAEMON_WALLET_H +#include "config.h" + +struct wallet; +struct lightningd_state; +struct bitcoin_tx; +struct bitcoin_tx_output; + +void wallet_add_signed_input(struct lightningd_state *dstate, + const struct wallet *w, + struct bitcoin_tx *tx, + unsigned int input_num); + +struct wallet *wallet_can_spend(struct lightningd_state *dstate, + const struct bitcoin_tx_output *output); + +#endif /* LIGHTNING_DAEMON_WALLET_H */