/* 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 "db.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; }; bool restore_wallet_address(struct lightningd_state *dstate, const struct privkey *privkey) { struct wallet *w = tal(dstate, struct wallet); u8 *redeemscript; struct sha256 h; w->privkey = *privkey; if (!pubkey_from_privkey(&w->privkey, &w->pubkey)) return false; redeemscript = bitcoin_redeem_p2wpkh(w, &w->pubkey); sha256(&h, redeemscript, tal_count(redeemscript)); ripemd160(&w->p2sh, h.u.u8, sizeof(h)); list_add_tail(&dstate->wallet, &w->list); tal_free(redeemscript); return true; } static void new_keypair(struct privkey *privkey, struct pubkey *pubkey) { do { randombytes_buf(privkey->secret, sizeof(privkey->secret)); } while (!pubkey_from_privkey(privkey, pubkey)); } static struct wallet *find_by_pubkey(struct lightningd_state *dstate, const struct pubkey *walletkey) { struct wallet *w; list_for_each(&dstate->wallet, w, list) { if (pubkey_eq(walletkey, &w->pubkey)) return w; } return NULL; } bool wallet_add_signed_input(struct lightningd_state *dstate, const struct pubkey *walletkey, struct bitcoin_tx *tx, unsigned int input_num) { u8 *redeemscript; struct bitcoin_signature sig; struct wallet *w = find_by_pubkey(dstate, walletkey); assert(input_num < tx->input_count); if (!w) return false; redeemscript = bitcoin_redeem_p2wpkh(tx, &w->pubkey); sig.stype = SIGHASH_ALL; sign_tx_input(tx, input_num, redeemscript, tal_count(redeemscript), p2wpkh_scriptcode(redeemscript, &w->pubkey), &w->privkey, &w->pubkey, &sig.sig); bitcoin_witness_p2sh_p2wpkh(tx->input, &tx->input[input_num], &sig, &w->pubkey); tal_free(redeemscript); return true; } bool wallet_can_spend(struct lightningd_state *dstate, const struct bitcoin_tx_output *output, struct pubkey *walletkey) { 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)) { *walletkey = w->pubkey; return true; } } return false; } 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(&w->privkey, &w->pubkey); redeemscript = bitcoin_redeem_p2wpkh(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); db_add_wallet_privkey(cmd->dstate, &w->privkey); json_object_start(response, NULL); json_add_string(response, "address", p2sh_to_base58(cmd, cmd->dstate->testnet, &w->p2sh)); json_object_end(response); command_success(cmd, response); } static const struct json_command newaddr_command = { "newaddr", json_newaddr, "Get a new address to fund a channel", "Returns {address} a p2sh address" }; AUTODATA(json_command, &newaddr_command);