core-lightning/wallet/walletrpc.c
Rusty Russell 91a22dc496 jsonprc: make json_get_params() fail the command, for better error reporting.
We move it into jsonrpc where it belongs, and make it fail the command.
This means it can tell us exactly what was wrong.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-02-02 00:05:00 +01:00

455 lines
14 KiB
C

#include <bitcoin/address.h>
#include <bitcoin/base58.h>
#include <bitcoin/script.h>
#include <ccan/tal/str/str.h>
#include <common/bech32.h>
#include <common/key_derive.h>
#include <common/status.h>
#include <common/utxo.h>
#include <common/withdraw_tx.h>
#include <errno.h>
#include <hsmd/gen_hsm_client_wire.h>
#include <inttypes.h>
#include <lightningd/bitcoind.h>
#include <lightningd/chaintopology.h>
#include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/subd.h>
#include <wally_bip32.h>
#include <wire/wire_sync.h>
struct withdrawal {
u64 amount, changesatoshi;
u8 *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 = withdraw->cmd->ld;
struct bitcoin_tx *tx;
u64 change_satoshi = 0;
/* 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) {
/* Mark used outputs as spent */
wallet_confirm_utxos(ld->wallet, withdraw->utxos);
/* Parse the tx and extract the change output. We
* generated the hex tx, so this should always work */
tx = bitcoin_tx_from_hex(withdraw, withdraw->hextx, strlen(withdraw->hextx));
assert(tx != NULL);
wallet_extract_owned_outputs(ld->wallet, tx, &change_satoshi);
/* Note normally, change_satoshi == withdraw->changesatoshi, but
* not if we're actually making a payment to ourselves! */
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);
}
}
/**
* segwit_addr_net_decode - Try to decode a Bech32 address and detect
* testnet/mainnet
*
* This processes the address and returns true if it is a Bech32
* address specified by BIP173. If it returns true, then *testnet is
* set whether it is testnet "tb" address or false if mainnet "bc"
* address. It does not check, witness version and program size
* restrictions.
*
* Out: testnet: Pointer to a bool that will be updated to true if the
* address is testnet, or false if mainnet.
* witness_version: Pointer to an int that will be updated to contain
* the witness program version (between 0 and 16 inclusive).
* witness_program: Pointer to a buffer of size 40 that will be updated
* to contain the witness program bytes.
* witness_program_len: Pointer to a size_t that will be updated to
* contain the length of bytes in witness_program.
* In: addrz: Pointer to the null-terminated address.
* Returns true if successful, false if fail (on fail, none of the out
* parameters are modified).
*/
static bool segwit_addr_net_decode(bool *testnet, int *witness_version,
uint8_t *witness_program,
size_t *witness_program_len,
const char *addrz)
{
/* segwit_addr_decode itself expects a prog buffer (which we pass
* witness_program as) of size 40, so segwit_addr_net_decode
* inherits that requirement. It will not write to that buffer
* if the input address is too long, so no buffer overflow risk. */
if (segwit_addr_decode(witness_version,
witness_program, witness_program_len,
"bc", addrz)) {
*testnet = false;
return true;
} else if (segwit_addr_decode(witness_version,
witness_program, witness_program_len,
"tb", addrz)) {
*testnet = true;
return true;
}
return false;
}
/**
* scriptpubkey_from_address - Determine scriptpubkey from a given address
*
* This processes the address and returns the equivalent scriptpubkey
* for the address. If fail to parse the address, return NULL. If can
* parse address, also sets the testnet flag if address is a testnet
* address or clears it if mainnet.
*/
static u8 *scriptpubkey_from_address(const tal_t *cxt, bool *testnet,
const char *addr, size_t addrlen)
{
struct bitcoin_address p2pkh_destination;
struct ripemd160 p2sh_destination;
int witness_version;
/* segwit_addr_net_decode requires a buffer of size 40, and will
* not write to the buffer if the address is too long, so a buffer
* of fixed size 40 will not overflow. */
uint8_t witness_program[40];
size_t witness_program_len;
bool witness_ok;
u8 *script = NULL;
char *addrz;
bool my_testnet;
if (bitcoin_from_base58(testnet, &p2pkh_destination,
addr, addrlen)) {
script = scriptpubkey_p2pkh(cxt, &p2pkh_destination);
} else if (p2sh_from_base58(testnet, &p2sh_destination,
addr, addrlen)) {
script = scriptpubkey_p2sh_hash(cxt, &p2sh_destination);
}
/* Insert other parsers that accept pointer+len here. */
if (script) return script;
/* Generate null-terminated address. */
addrz = tal_dup_arr(cxt, char, addr, addrlen, 1);
addrz[addrlen] = '\0';
if (segwit_addr_net_decode(&my_testnet, &witness_version,
witness_program, &witness_program_len,
addrz)) {
witness_ok = false;
if (witness_version == 0 && (witness_program_len == 20 ||
witness_program_len == 32)) {
witness_ok = true;
}
/* Insert other witness versions here. */
if (witness_ok) {
*testnet = my_testnet;
script = scriptpubkey_witness_raw(cxt, witness_version,
witness_program,
witness_program_len);
}
}
/* Insert other parsers that accept null-terminated string here. */
tal_free(addrz);
return script;
}
/**
* 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)
{
jsmntok_t *desttok, *sattok;
struct withdrawal *withdraw;
bool testnet;
u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL);
u64 fee_estimate;
struct utxo *utxos;
struct bitcoin_tx *tx;
bool withdraw_all = false;
if (!json_get_params(cmd, buffer, params,
"destination", &desttok,
"satoshi", &sattok,
NULL)) {
return;
}
withdraw = tal(cmd, struct withdrawal);
withdraw->cmd = cmd;
if (json_tok_streq(buffer, sattok, "all"))
withdraw_all = true;
else if (!json_tok_u64(buffer, sattok, &withdraw->amount)) {
command_fail(cmd, "Invalid satoshis");
return;
}
/* Parse address. */
withdraw->destination
= scriptpubkey_from_address(withdraw, &testnet,
buffer + desttok->start,
desttok->end - desttok->start);
/* Check that destination address could be understood. */
if (!withdraw->destination) {
command_fail(cmd, "Could not parse destination address");
return;
}
/* Check address given is compatible with the chain we are on. */
if (testnet != get_chainparams(cmd->ld)->testnet) {
if (testnet) {
command_fail(cmd,
"Use of testnet address on mainnet");
} else {
command_fail(cmd,
"Use of mainnet address on testnet");
}
return;
}
/* Select the coins */
if (withdraw_all) {
withdraw->utxos = wallet_select_all(cmd, cmd->ld->wallet,
feerate_per_kw,
tal_len(withdraw->destination),
&withdraw->amount,
&fee_estimate);
/* FIXME Pull dust amount from the daemon config */
if (!withdraw->utxos || withdraw->amount < 546) {
command_fail(cmd, "Cannot afford fee %"PRIu64,
fee_estimate);
return;
}
withdraw->changesatoshi = 0;
} else {
withdraw->utxos = wallet_select_coins(cmd, cmd->ld->wallet,
withdraw->amount,
feerate_per_kw,
tal_len(withdraw->destination),
&fee_estimate,
&withdraw->changesatoshi);
if (!withdraw->utxos) {
command_fail(cmd, "Not enough funds available");
return;
}
}
/* FIXME(cdecker) Pull this from the daemon config */
if (withdraw->changesatoshi <= 546)
withdraw->changesatoshi = 0;
if (withdraw->changesatoshi)
withdraw->change_key_index = wallet_get_newindex(cmd->ld);
else
withdraw->change_key_index = 0;
utxos = from_utxoptr_arr(withdraw, withdraw->utxos);
u8 *msg = towire_hsm_sign_withdrawal(cmd,
withdraw->amount,
withdraw->changesatoshi,
withdraw->change_key_index,
withdraw->destination,
utxos);
tal_free(utxos);
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
fatal("Could not write sign_withdrawal to HSM: %s",
strerror(errno));
msg = hsm_sync_read(cmd, cmd->ld);
tx = tal(withdraw, struct bitcoin_tx);
if (!fromwire_hsm_sign_withdrawal_reply(msg, NULL, tx))
fatal("HSM gave bad sign_withdrawal_reply %s",
tal_hex(withdraw, msg));
/* Now broadcast the transaction */
withdraw->hextx = tal_hex(withdraw, linearize_tx(cmd, tx));
bitcoind_sendrawtx(cmd->ld->topology->bitcoind, withdraw->hextx,
wallet_withdrawal_broadcast, withdraw);
command_still_pending(cmd);
}
static const struct json_command withdraw_command = {
"withdraw",
json_withdraw,
"Send to {destination} address {satoshi} (or 'all') amount via Bitcoin transaction"
};
AUTODATA(json_command, &withdraw_command);
static void json_newaddr(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
struct json_result *response = new_json_result(cmd);
struct ext_key ext;
struct sha256 h;
struct ripemd160 p2sh;
struct pubkey pubkey;
u8 *redeemscript;
s64 keyidx;
keyidx = wallet_get_newindex(cmd->ld);
if (keyidx < 0) {
command_fail(cmd, "Keys exhausted ");
return;
}
if (bip32_key_from_parent(cmd->ld->wallet->bip32_base, keyidx,
BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) {
command_fail(cmd, "Keys generation failure");
return;
}
if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &pubkey.pubkey,
ext.pub_key, sizeof(ext.pub_key))) {
command_fail(cmd, "Key parsing failure");
return;
}
txfilter_add_derkey(cmd->ld->owned_txfilter, ext.pub_key);
redeemscript = bitcoin_redeem_p2sh_p2wpkh(cmd, &pubkey);
sha256(&h, redeemscript, tal_count(redeemscript));
ripemd160(&p2sh, h.u.u8, sizeof(h));
json_object_start(response, NULL);
json_add_string(response, "address",
p2sh_to_base58(cmd, get_chainparams(cmd->ld)->testnet,
&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"
};
AUTODATA(json_command, &newaddr_command);
static void json_listfunds(struct command *cmd, const char *buffer,
const jsmntok_t *params)
{
struct json_result *response = new_json_result(cmd);
struct utxo **utxos =
wallet_get_utxos(cmd, cmd->ld->wallet, output_state_available);
json_object_start(response, NULL);
json_array_start(response, "outputs");
for (int i = 0; i < tal_count(utxos); i++) {
json_object_start(response, NULL);
json_add_txid(response, "txid", &utxos[i]->txid);
json_add_num(response, "output", utxos[i]->outnum);
json_add_u64(response, "value", utxos[i]->amount);
json_object_end(response);
}
json_array_end(response);
json_object_end(response);
command_success(cmd, response);
}
static const struct json_command listfunds_command = {
"listfunds",
json_listfunds,
"Show funds available for opening channels"
};
AUTODATA(json_command, &listfunds_command);
struct txo_rescan {
struct command *cmd;
struct utxo **utxos;
struct json_result *response;
};
static void process_utxo_result(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *txout,
void *arg)
{
struct txo_rescan *rescan = arg;
struct json_result *response = rescan->response;
struct utxo *u = rescan->utxos[0];
enum output_status newstate =
txout == NULL ? output_state_spent : output_state_available;
json_object_start(rescan->response, NULL);
json_add_txid(response, "txid", &u->txid);
json_add_num(response, "output", u->outnum);
json_add_num(response, "oldstate", u->status);
json_add_num(response, "newstate", newstate);
json_object_end(rescan->response);
wallet_update_output_status(bitcoind->ld->wallet, &u->txid, u->outnum,
u->status, newstate);
/* Remove the utxo we just resolved */
rescan->utxos[0] = rescan->utxos[tal_count(rescan->utxos) - 1];
tal_resize(&rescan->utxos, tal_count(rescan->utxos) - 1);
if (tal_count(rescan->utxos) == 0) {
/* Complete the response */
json_array_end(rescan->response);
json_object_end(rescan->response);
command_success(rescan->cmd, rescan->response);
} else {
bitcoind_gettxout(
bitcoind->ld->topology->bitcoind, &rescan->utxos[0]->txid,
rescan->utxos[0]->outnum, process_utxo_result, rescan);
}
}
static void json_dev_rescan_outputs(struct command *cmd, const char *buffer,
const jsmntok_t *params)
{
struct txo_rescan *rescan = tal(cmd, struct txo_rescan);
rescan->response = new_json_result(cmd);
rescan->cmd = cmd;
/* Open the result structure so we can incrementally add results */
json_object_start(rescan->response, NULL);
json_array_start(rescan->response, "outputs");
rescan->utxos =
wallet_get_utxos(rescan, cmd->ld->wallet, output_state_any);
bitcoind_gettxout(cmd->ld->topology->bitcoind, &rescan->utxos[0]->txid,
rescan->utxos[0]->outnum, process_utxo_result,
rescan);
command_still_pending(cmd);
}
static const struct json_command dev_rescan_output_command = {
"dev-rescan-outputs", json_dev_rescan_outputs,
"Synchronize the state of our funds with bitcoind"
};
AUTODATA(json_command, &dev_rescan_output_command);