Christian Decker
8 years ago
committed by
Rusty Russell
3 changed files with 204 additions and 3 deletions
@ -0,0 +1,189 @@ |
|||||
|
#include <bitcoin/address.h> |
||||
|
#include <bitcoin/base58.h> |
||||
|
#include <bitcoin/script.h> |
||||
|
#include <ccan/tal/str/str.h> |
||||
|
#include <daemon/bitcoind.h> |
||||
|
#include <daemon/chaintopology.h> |
||||
|
#include <daemon/jsonrpc.h> |
||||
|
#include <lightningd/hsm/gen_hsm_wire.h> |
||||
|
#include <lightningd/key_derive.h> |
||||
|
#include <lightningd/lightningd.h> |
||||
|
#include <lightningd/subd.h> |
||||
|
#include <lightningd/utxo.h> |
||||
|
#include <lightningd/withdraw_tx.h> |
||||
|
#include <permute_tx.h> |
||||
|
#include <wally_bip32.h> |
||||
|
|
||||
|
struct withdrawal { |
||||
|
u64 amount, changesatoshi; |
||||
|
struct bitcoin_address destination; |
||||
|
const struct utxo **utxos; |
||||
|
u64 change_key_index; |
||||
|
struct command *cmd; |
||||
|
const char *hextx; |
||||
|
}; |
||||
|
|
||||
|
/**
|
||||
|
* wallet_withdrawal_broadcast - The tx has been broadcast (or it failed) |
||||
|
* |
||||
|
* This is the final step in the withdrawal. We either successfully |
||||
|
* broadcast the withdrawal transaction or it failed somehow. So we |
||||
|
* report success or a broadcast failure. Upon success we also mark |
||||
|
* the used outputs as spent, and add the change output to our pool of |
||||
|
* available outputs. |
||||
|
*/ |
||||
|
static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind, |
||||
|
int exitstatus, const char *msg, |
||||
|
struct withdrawal *withdraw) |
||||
|
{ |
||||
|
struct command *cmd = withdraw->cmd; |
||||
|
struct lightningd *ld = ld_from_dstate(withdraw->cmd->dstate); |
||||
|
|
||||
|
/* Massage output into shape so it doesn't kill the JSON serialization */ |
||||
|
char *output = tal_strjoin(cmd, tal_strsplit(cmd, msg, "\n", STR_NO_EMPTY), " ", STR_NO_TRAIL); |
||||
|
if (exitstatus == 0) { |
||||
|
wallet_confirm_utxos(ld->wallet, withdraw->utxos); |
||||
|
/* TODO(cdecker) Add the change output to the database */ |
||||
|
struct json_result *response = new_json_result(cmd); |
||||
|
json_object_start(response, NULL); |
||||
|
json_add_string(response, "tx", withdraw->hextx); |
||||
|
json_add_string(response, "txid", output); |
||||
|
json_object_end(response); |
||||
|
command_success(cmd, response); |
||||
|
} else { |
||||
|
command_fail(cmd, "Error broadcasting transaction: %s", output); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* wallet_withdrawal_signed - The HSM has signed our withdrawal request |
||||
|
* |
||||
|
* This is the second step (2/3) of the withdrawal flow. The HSM has |
||||
|
* returned the necessary signatures for the withdrawal transaction, |
||||
|
* so now we can assemble the transaction and kick off the broadcast. |
||||
|
*/ |
||||
|
static bool wallet_withdrawal_signed(struct subd *hsm, const u8 *reply, |
||||
|
const int *fds, |
||||
|
struct withdrawal *withdraw) |
||||
|
{ |
||||
|
struct command *cmd = withdraw->cmd; |
||||
|
struct lightningd *ld = ld_from_dstate(cmd->dstate); |
||||
|
struct ext_key ext; |
||||
|
struct pubkey changekey; |
||||
|
secp256k1_ecdsa_signature *sigs; |
||||
|
struct bitcoin_tx *tx; |
||||
|
|
||||
|
if (!fromwire_hsmctl_sign_withdrawal_reply(withdraw, reply, NULL, &sigs)) |
||||
|
fatal("HSM gave bad sign_withdrawal_reply %s", |
||||
|
tal_hex(withdraw, reply)); |
||||
|
|
||||
|
if (bip32_key_from_parent(ld->bip32_base, withdraw->change_key_index, |
||||
|
BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) { |
||||
|
command_fail(cmd, "Changekey generation failure"); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
pubkey_from_der(ext.pub_key, sizeof(ext.pub_key), &changekey); |
||||
|
tx = withdraw_tx(withdraw, withdraw->utxos, &withdraw->destination, |
||||
|
withdraw->amount, &changekey, withdraw->changesatoshi, |
||||
|
ld->bip32_base); |
||||
|
|
||||
|
if (tal_count(sigs) != tal_count(tx->input)) |
||||
|
fatal("HSM gave %zu sigs, needed %zu", |
||||
|
tal_count(sigs), tal_count(tx->input)); |
||||
|
|
||||
|
/* Create input parts from signatures. */ |
||||
|
for (size_t i = 0; i < tal_count(tx->input); i++) { |
||||
|
struct pubkey key; |
||||
|
|
||||
|
if (!bip32_pubkey(hsm->ld->bip32_base, |
||||
|
&key, withdraw->utxos[i]->keyindex)) |
||||
|
fatal("Cannot generate BIP32 key for UTXO %u", |
||||
|
withdraw->utxos[i]->keyindex); |
||||
|
|
||||
|
/* P2SH inputs have same witness. */ |
||||
|
tx->input[i].witness |
||||
|
= bitcoin_witness_p2wpkh(tx, &sigs[i], &key); |
||||
|
} |
||||
|
|
||||
|
/* Now broadcast the transaction */ |
||||
|
withdraw->hextx = tal_hex(withdraw, linearize_tx(cmd, tx)); |
||||
|
bitcoind_sendrawtx(ld->topology->bitcoind, withdraw->hextx, |
||||
|
wallet_withdrawal_broadcast, withdraw); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* json_withdraw - Entrypoint for the withdrawal flow |
||||
|
* |
||||
|
* A user has requested a withdrawal over the JSON-RPC, parse the |
||||
|
* request, select coins and a change key. Then send the request to |
||||
|
* the HSM to generate the signatures. |
||||
|
*/ |
||||
|
static void json_withdraw(struct command *cmd, |
||||
|
const char *buffer, const jsmntok_t *params) |
||||
|
{ |
||||
|
struct lightningd *ld = ld_from_dstate(cmd->dstate); |
||||
|
jsmntok_t *desttok, *sattok; |
||||
|
struct withdrawal *withdraw; |
||||
|
bool testnet; |
||||
|
/* FIXME: Read feerate and dustlimit */ |
||||
|
u32 feerate_per_kw = 15000; |
||||
|
//u64 dust_limit = 600;
|
||||
|
u64 fee_estimate; |
||||
|
struct utxo *utxos; |
||||
|
if (!json_get_params(buffer, params, |
||||
|
"destination", &desttok, |
||||
|
"satoshi", &sattok, |
||||
|
NULL)) { |
||||
|
command_fail(cmd, "Need destination and satoshi."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
withdraw = tal(cmd, struct withdrawal); |
||||
|
withdraw->cmd = cmd; |
||||
|
|
||||
|
if (!json_tok_u64(buffer, sattok, &withdraw->amount)) { |
||||
|
command_fail(cmd, "Invalid satoshis"); |
||||
|
return; |
||||
|
} |
||||
|
if (!bitcoin_from_base58(&testnet, &withdraw->destination, |
||||
|
buffer + desttok->start, |
||||
|
desttok->end - desttok->start)) { |
||||
|
command_fail(cmd, "Could not parse destination address"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
/* Select the coins */ |
||||
|
withdraw->utxos = wallet_select_coins(cmd, ld->wallet, withdraw->amount, |
||||
|
feerate_per_kw, &fee_estimate, |
||||
|
&withdraw->changesatoshi); |
||||
|
if (!withdraw->utxos) { |
||||
|
command_fail(cmd, "Not enough funds available"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
withdraw->change_key_index = |
||||
|
db_get_intvar(ld->wallet->db, "bip32_max_index", 0) + 1; |
||||
|
db_set_intvar(ld->wallet->db, "bip32_max_index", |
||||
|
withdraw->change_key_index); |
||||
|
|
||||
|
utxos = from_utxoptr_arr(withdraw, withdraw->utxos); |
||||
|
u8 *msg = towire_hsmctl_sign_withdrawal(cmd, |
||||
|
withdraw->amount, |
||||
|
withdraw->changesatoshi, |
||||
|
withdraw->change_key_index, |
||||
|
withdraw->destination.addr.u.u8, |
||||
|
utxos); |
||||
|
subd_req(cmd, ld->hsm, take(msg), -1, 0, wallet_withdrawal_signed, |
||||
|
withdraw); |
||||
|
tal_free(utxos); |
||||
|
} |
||||
|
|
||||
|
static const struct json_command withdraw_command = { |
||||
|
"withdraw", |
||||
|
json_withdraw, |
||||
|
"Send {satoshi} to the {destination} address via Bitcoin transaction", |
||||
|
"Returns the withdrawal transaction ID" |
||||
|
}; |
||||
|
AUTODATA(json_command, &withdraw_command); |
@ -0,0 +1,11 @@ |
|||||
|
#ifndef WALLET_WALLETRPC_H |
||||
|
#define WALLET_WALLETRPC_H |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
/**
|
||||
|
* Placeholder file, we aren't really exposing anything directly. RPC |
||||
|
* handlers are registered using AUTODATA. |
||||
|
*/ |
||||
|
|
||||
|
#endif /* WALLET_WALLETRPC_H */ |
Loading…
Reference in new issue