2017-04-01 15:05:29 +02:00
# include "pay.h"
# include <bitcoin/preimage.h>
# include <ccan/str/hex/hex.h>
# include <ccan/structeq/structeq.h>
2017-10-26 05:07:19 +02:00
# include <ccan/tal/str/str.h>
2017-08-29 06:12:04 +02:00
# include <channeld/gen_channel_wire.h>
2017-11-22 01:25:39 +01:00
# include <common/bolt11.h>
2017-10-26 05:07:19 +02:00
# include <gossipd/gen_gossip_wire.h>
2017-10-26 04:56:19 +02:00
# include <gossipd/routing.h>
2017-04-01 15:05:29 +02:00
# include <inttypes.h>
2017-08-28 18:04:01 +02:00
# include <lightningd/chaintopology.h>
# include <lightningd/jsonrpc.h>
2017-04-01 15:05:29 +02:00
# include <lightningd/lightningd.h>
2017-08-28 18:04:01 +02:00
# include <lightningd/log.h>
2018-01-19 10:53:24 +01:00
# include <lightningd/options.h>
2017-04-01 15:05:29 +02:00
# include <lightningd/peer_control.h>
2017-06-20 08:12:03 +02:00
# include <lightningd/peer_htlcs.h>
2017-04-01 15:05:29 +02:00
# include <lightningd/subd.h>
# include <sodium/randombytes.h>
static void json_pay_success ( struct command * cmd , const struct preimage * rval )
{
struct json_result * response ;
/* Can be NULL if JSON RPC goes away. */
if ( ! cmd )
return ;
response = new_json_result ( cmd ) ;
json_object_start ( response , NULL ) ;
json_add_hex ( response , " preimage " , rval , sizeof ( * rval ) ) ;
json_object_end ( response ) ;
command_success ( cmd , response ) ;
}
2018-01-17 21:29:49 +01:00
static void json_pay_failed ( struct command * cmd ,
2017-04-01 15:05:29 +02:00
const struct pubkey * sender ,
enum onion_type failure_code ,
const char * details )
{
/* Can be NULL if JSON RPC goes away. */
2018-01-17 21:29:49 +01:00
if ( cmd ) {
/* FIXME: Report sender! */
command_fail ( cmd , " failed: %s (%s) " ,
onion_type_name ( failure_code ) , details ) ;
}
2017-04-01 15:05:29 +02:00
}
2017-06-20 07:53:03 +02:00
void payment_succeeded ( struct lightningd * ld , struct htlc_out * hout ,
2017-04-01 15:05:29 +02:00
const struct preimage * rval )
{
2018-01-17 21:29:49 +01:00
wallet_payment_set_status ( ld - > wallet , & hout - > payment_hash ,
PAYMENT_COMPLETE , rval ) ;
2018-01-17 21:29:49 +01:00
/* Can be NULL if JSON RPC goes away. */
if ( hout - > cmd )
json_pay_success ( hout - > cmd , rval ) ;
2018-01-17 21:29:49 +01:00
hout - > cmd = NULL ;
2017-04-01 15:05:29 +02:00
}
2017-06-20 08:12:03 +02:00
void payment_failed ( struct lightningd * ld , const struct htlc_out * hout ,
const char * localfail )
2017-04-01 15:05:29 +02:00
{
2017-06-20 07:53:03 +02:00
struct onionreply * reply ;
2017-11-28 06:03:04 +01:00
enum onion_type failcode ;
2018-01-17 21:29:49 +01:00
struct secret * path_secrets ;
const tal_t * tmpctx = tal_tmpctx ( ld ) ;
2017-06-20 07:53:03 +02:00
2018-01-17 21:29:49 +01:00
wallet_payment_set_status ( ld - > wallet , & hout - > payment_hash ,
PAYMENT_FAILED , NULL ) ;
2017-11-10 21:21:09 +01:00
2017-11-28 06:03:04 +01:00
/* This gives more details than a generic failure message */
2017-06-20 08:12:03 +02:00
if ( localfail ) {
2018-01-17 21:29:49 +01:00
json_pay_failed ( hout - > cmd , NULL , hout - > failcode , localfail ) ;
2018-01-17 21:29:49 +01:00
tal_free ( tmpctx ) ;
2017-06-20 08:12:03 +02:00
return ;
}
2017-11-28 06:03:04 +01:00
/* Must be remote fail. */
assert ( ! hout - > failcode ) ;
2018-01-17 21:29:49 +01:00
path_secrets = wallet_payment_get_secrets ( tmpctx , ld - > wallet ,
& hout - > payment_hash ) ;
2018-01-17 21:29:49 +01:00
reply = unwrap_onionreply ( tmpctx , path_secrets , tal_count ( path_secrets ) ,
2017-11-28 06:03:04 +01:00
hout - > failuremsg ) ;
if ( ! reply ) {
log_info ( hout - > key . peer - > log ,
" htlc % " PRIu64 " failed with bad reply (%s) " ,
hout - > key . id ,
2017-12-15 11:29:25 +01:00
tal_hex ( ltmp , hout - > failuremsg ) ) ;
2017-11-28 06:03:04 +01:00
failcode = WIRE_PERMANENT_NODE_FAILURE ;
} else {
failcode = fromwire_peektype ( reply - > msg ) ;
log_info ( hout - > key . peer - > log ,
" htlc % " PRIu64 " failed from %ith node with code 0x%04x (%s) " ,
hout - > key . id ,
reply - > origin_index ,
failcode , onion_type_name ( failcode ) ) ;
2017-06-20 07:53:03 +02:00
}
/* FIXME: save ids we can turn reply->origin_index into sender. */
2017-04-01 15:05:29 +02:00
/* FIXME: check for routing failure / perm fail. */
/* check_for_routing_failure(i, sender, failure_code); */
2017-06-20 07:53:03 +02:00
2018-01-17 21:29:49 +01:00
json_pay_failed ( hout - > cmd , NULL , failcode , " reply from remote " ) ;
2018-01-17 21:29:49 +01:00
tal_free ( tmpctx ) ;
2017-04-01 15:05:29 +02:00
}
2018-01-17 21:29:49 +01:00
/* When JSON RPC goes away, cmd is freed: detach from the hout */
static void remove_cmd_from_hout ( struct command * cmd , struct htlc_out * hout )
2017-04-01 15:05:29 +02:00
{
2018-01-17 21:29:49 +01:00
assert ( hout - > cmd = = cmd ) ;
hout - > cmd = NULL ;
2017-04-01 15:05:29 +02:00
}
2017-12-15 11:15:54 +01:00
/* Returns true if it's still pending. */
static bool send_payment ( struct command * cmd ,
2017-10-26 04:56:19 +02:00
const struct sha256 * rhash ,
const struct route_hop * route )
2017-04-01 15:05:29 +02:00
{
2017-10-26 04:56:19 +02:00
struct peer * peer ;
2017-04-01 15:05:29 +02:00
const u8 * onion ;
u8 sessionkey [ 32 ] ;
2017-10-26 04:56:19 +02:00
unsigned int base_expiry ;
2017-04-01 15:05:29 +02:00
struct onionpacket * packet ;
2017-05-06 04:19:44 +02:00
struct secret * path_secrets ;
2017-06-20 08:14:03 +02:00
enum onion_type failcode ;
2017-12-15 11:29:41 +01:00
/* Freed automatically on cmd completion: only manually at end. */
const tal_t * tmpctx = tal_tmpctx ( cmd ) ;
2017-10-26 04:56:19 +02:00
size_t i , n_hops = tal_count ( route ) ;
2017-12-15 11:29:41 +01:00
struct hop_data * hop_data = tal_arr ( tmpctx , struct hop_data , n_hops ) ;
struct pubkey * ids = tal_arr ( tmpctx , struct pubkey , n_hops ) ;
2017-11-23 21:54:19 +01:00
struct wallet_payment * payment = NULL ;
2018-01-17 21:29:49 +01:00
struct htlc_out * hout ;
2017-04-01 15:05:29 +02:00
/* Expiry for HTLCs is absolute. And add one to give some margin. */
2017-08-28 18:09:01 +02:00
base_expiry = get_block_height ( cmd - > ld - > topology ) + 1 ;
2017-04-01 15:05:29 +02:00
2017-10-26 04:56:19 +02:00
/* Extract IDs for each hop: create_onionpacket wants array. */
for ( i = 0 ; i < n_hops ; i + + )
ids [ i ] = route [ i ] . nodeid ;
2017-04-01 15:05:29 +02:00
2017-10-26 04:56:19 +02:00
/* Copy hop_data[n] from route[n+1] (ie. where it goes next) */
for ( i = 0 ; i < n_hops - 1 ; i + + ) {
hop_data [ i ] . realm = 0 ;
hop_data [ i ] . channel_id = route [ i + 1 ] . channel_id ;
hop_data [ i ] . amt_forward = route [ i + 1 ] . amount ;
hop_data [ i ] . outgoing_cltv = base_expiry + route [ i + 1 ] . delay ;
2017-04-01 15:05:29 +02:00
}
2017-04-30 16:29:31 +02:00
/* And finally set the final hop to the special values in
* BOLT04 */
2017-10-26 04:56:19 +02:00
hop_data [ i ] . realm = 0 ;
hop_data [ i ] . outgoing_cltv = base_expiry + route [ i ] . delay ;
memset ( & hop_data [ i ] . channel_id , 0 , sizeof ( struct short_channel_id ) ) ;
hop_data [ i ] . amt_forward = route [ i ] . amount ;
2017-04-01 15:05:29 +02:00
2018-01-17 21:29:49 +01:00
/* Now, do we already have a payment? */
payment = wallet_payment_by_hash ( tmpctx , cmd - > ld - > wallet , rhash ) ;
if ( payment ) {
2018-01-17 21:29:49 +01:00
/* FIXME: We should really do something smarter here! */
2017-08-28 18:09:01 +02:00
log_debug ( cmd - > ld - > log , " json_sendpay: found previous " ) ;
2018-01-17 21:29:49 +01:00
if ( payment - > status = = PAYMENT_PENDING ) {
2017-08-28 18:09:01 +02:00
log_add ( cmd - > ld - > log , " ... still in progress " ) ;
2017-04-01 15:05:29 +02:00
command_fail ( cmd , " still in progress " ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-04-01 15:05:29 +02:00
}
2018-01-17 21:29:49 +01:00
if ( payment - > status = = PAYMENT_COMPLETE ) {
2017-08-28 18:09:01 +02:00
log_add ( cmd - > ld - > log , " ... succeeded " ) ;
2017-04-01 15:05:29 +02:00
/* Must match successful payment parameters. */
2018-01-17 21:29:49 +01:00
if ( payment - > msatoshi ! = hop_data [ n_hops - 1 ] . amt_forward ) {
2017-04-01 15:05:29 +02:00
command_fail ( cmd ,
" already succeeded with amount % "
2018-01-17 21:29:49 +01:00
PRIu64 , payment - > msatoshi ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-04-01 15:05:29 +02:00
}
2018-01-17 21:29:49 +01:00
if ( ! structeq ( & payment - > destination , & ids [ n_hops - 1 ] ) ) {
2017-04-01 15:05:29 +02:00
command_fail ( cmd ,
" already succeeded to %s " ,
2018-01-17 21:29:49 +01:00
type_to_string ( cmd , struct pubkey ,
& payment - > destination ) ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-04-01 15:05:29 +02:00
}
2018-01-17 21:29:49 +01:00
json_pay_success ( cmd , payment - > payment_preimage ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-04-01 15:05:29 +02:00
}
2018-01-17 21:29:49 +01:00
wallet_payment_delete ( cmd - > ld - > wallet , rhash ) ;
2017-08-28 18:09:01 +02:00
log_add ( cmd - > ld - > log , " ... retrying " ) ;
2017-04-01 15:05:29 +02:00
}
2017-08-28 18:09:01 +02:00
peer = peer_by_id ( cmd - > ld , & ids [ 0 ] ) ;
2017-04-01 15:05:29 +02:00
if ( ! peer ) {
command_fail ( cmd , " no connection to first peer found " ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-04-01 15:05:29 +02:00
}
randombytes_buf ( & sessionkey , sizeof ( sessionkey ) ) ;
/* Onion will carry us from first peer onwards. */
2017-10-26 04:56:19 +02:00
packet = create_onionpacket ( cmd , ids , hop_data , sessionkey , rhash - > u . u8 ,
2017-05-03 11:01:58 +02:00
sizeof ( struct sha256 ) , & path_secrets ) ;
2017-04-01 15:05:29 +02:00
onion = serialize_onionpacket ( cmd , packet ) ;
2018-01-17 21:29:49 +01:00
log_info ( cmd - > ld - > log , " Sending %u over %zu hops to deliver %u " ,
route [ 0 ] . amount , n_hops , route [ n_hops - 1 ] . amount ) ;
2017-10-26 04:56:19 +02:00
failcode = send_htlc_out ( peer , route [ 0 ] . amount ,
base_expiry + route [ 0 ] . delay ,
2018-01-17 21:29:49 +01:00
rhash , onion , NULL , cmd ,
2018-01-17 21:29:49 +01:00
& hout ) ;
2017-06-20 08:14:03 +02:00
if ( failcode ) {
command_fail ( cmd , " first peer not ready: %s " ,
onion_type_name ( failcode ) ) ;
2017-12-15 11:15:54 +01:00
return false ;
2017-06-20 08:14:03 +02:00
}
2018-01-17 21:29:49 +01:00
2018-01-17 21:29:49 +01:00
/* If hout fails, payment should be freed too. */
payment = tal ( hout , struct wallet_payment ) ;
payment - > id = 0 ;
payment - > payment_hash = * rhash ;
payment - > destination = ids [ n_hops - 1 ] ;
payment - > status = PAYMENT_PENDING ;
payment - > msatoshi = route [ n_hops - 1 ] . amount ;
payment - > timestamp = time_now ( ) . ts . tv_sec ;
payment - > payment_preimage = NULL ;
payment - > path_secrets = tal_steal ( payment , path_secrets ) ;
/* We write this into db when HTLC is actually sent. */
wallet_payment_setup ( cmd - > ld - > wallet , payment ) ;
2018-01-17 21:29:49 +01:00
/* If we fail, remove cmd ptr from htlc_out. */
2018-01-17 21:29:49 +01:00
tal_add_destructor2 ( cmd , remove_cmd_from_hout , hout ) ;
2018-01-17 21:29:49 +01:00
2017-12-15 11:29:41 +01:00
tal_free ( tmpctx ) ;
2017-12-15 11:15:54 +01:00
return true ;
2017-04-01 15:05:29 +02:00
}
2017-10-26 04:56:19 +02:00
static void json_sendpay ( struct command * cmd ,
const char * buffer , const jsmntok_t * params )
{
jsmntok_t * routetok , * rhashtok ;
const jsmntok_t * t , * end ;
size_t n_hops ;
struct sha256 rhash ;
struct route_hop * route ;
if ( ! json_get_params ( buffer , params ,
" route " , & routetok ,
" rhash " , & rhashtok ,
NULL ) ) {
command_fail ( cmd , " Need route and rhash " ) ;
return ;
}
if ( ! hex_decode ( buffer + rhashtok - > start ,
rhashtok - > end - rhashtok - > start ,
& rhash , sizeof ( rhash ) ) ) {
command_fail ( cmd , " '%.*s' is not a valid sha256 hash " ,
( int ) ( rhashtok - > end - rhashtok - > start ) ,
buffer + rhashtok - > start ) ;
return ;
}
if ( routetok - > type ! = JSMN_ARRAY ) {
command_fail ( cmd , " '%.*s' is not an array " ,
( int ) ( routetok - > end - routetok - > start ) ,
buffer + routetok - > start ) ;
return ;
}
end = json_next ( routetok ) ;
n_hops = 0 ;
route = tal_arr ( cmd , struct route_hop , n_hops ) ;
for ( t = routetok + 1 ; t < end ; t = json_next ( t ) ) {
const jsmntok_t * amttok , * idtok , * delaytok , * chantok ;
if ( t - > type ! = JSMN_OBJECT ) {
command_fail ( cmd , " route %zu '%.*s' is not an object " ,
n_hops ,
( int ) ( t - > end - t - > start ) ,
buffer + t - > start ) ;
return ;
}
amttok = json_get_member ( buffer , t , " msatoshi " ) ;
idtok = json_get_member ( buffer , t , " id " ) ;
delaytok = json_get_member ( buffer , t , " delay " ) ;
chantok = json_get_member ( buffer , t , " channel " ) ;
if ( ! amttok | | ! idtok | | ! delaytok | | ! chantok ) {
command_fail ( cmd , " route %zu needs msatoshi/id/channel/delay " ,
n_hops ) ;
return ;
}
tal_resize ( & route , n_hops + 1 ) ;
/* What that hop will forward */
if ( ! json_tok_number ( buffer , amttok , & route [ n_hops ] . amount ) ) {
command_fail ( cmd , " route %zu invalid msatoshi " ,
n_hops ) ;
return ;
}
2018-01-16 20:44:32 +01:00
if ( ! json_tok_short_channel_id ( buffer , chantok ,
2017-10-26 04:56:19 +02:00
& route [ n_hops ] . channel_id ) ) {
command_fail ( cmd , " route %zu invalid channel_id " , n_hops ) ;
return ;
}
if ( ! json_tok_pubkey ( buffer , idtok , & route [ n_hops ] . nodeid ) ) {
command_fail ( cmd , " route %zu invalid id " , n_hops ) ;
return ;
}
if ( ! json_tok_number ( buffer , delaytok , & route [ n_hops ] . delay ) ) {
command_fail ( cmd , " route %zu invalid delay " , n_hops ) ;
return ;
}
n_hops + + ;
}
if ( n_hops = = 0 ) {
command_fail ( cmd , " Empty route " ) ;
return ;
}
2017-12-15 11:15:54 +01:00
if ( send_payment ( cmd , & rhash , route ) )
command_still_pending ( cmd ) ;
2017-10-26 04:56:19 +02:00
}
2017-04-01 15:05:29 +02:00
static const struct json_command sendpay_command = {
" sendpay " ,
json_sendpay ,
" Send along {route} in return for preimage of {rhash} " ,
" Returns the {preimage} on success "
} ;
AUTODATA ( json_command , & sendpay_command ) ;
2017-10-26 05:07:19 +02:00
struct pay {
struct sha256 payment_hash ;
struct command * cmd ;
} ;
static void json_pay_getroute_reply ( struct subd * gossip ,
const u8 * reply , const int * fds ,
struct pay * pay )
{
struct route_hop * route ;
fromwire_gossip_getroute_reply ( reply , reply , NULL , & route ) ;
if ( tal_count ( route ) = = 0 ) {
command_fail ( pay - > cmd , " Could not find a route " ) ;
return ;
}
send_payment ( pay - > cmd , & pay - > payment_hash , route ) ;
}
static void json_pay ( struct command * cmd ,
const char * buffer , const jsmntok_t * params )
{
jsmntok_t * bolt11tok , * msatoshitok , * desctok , * riskfactortok ;
double riskfactor = 1.0 ;
u64 msatoshi ;
struct pay * pay = tal ( cmd , struct pay ) ;
struct bolt11 * b11 ;
char * fail , * b11str , * desc ;
u8 * req ;
if ( ! json_get_params ( buffer , params ,
" bolt11 " , & bolt11tok ,
" ?msatoshi " , & msatoshitok ,
" ?description " , & desctok ,
" ?riskfactor " , & riskfactortok ,
NULL ) ) {
command_fail ( cmd , " Need bolt11 string " ) ;
return ;
}
b11str = tal_strndup ( cmd , buffer + bolt11tok - > start ,
bolt11tok - > end - bolt11tok - > start ) ;
if ( desctok )
desc = tal_strndup ( cmd , buffer + desctok - > start ,
desctok - > end - desctok - > start ) ;
else
desc = NULL ;
b11 = bolt11_decode ( pay , b11str , desc , & fail ) ;
if ( ! b11 ) {
command_fail ( cmd , " Invalid bolt11: %s " , fail ) ;
return ;
}
pay - > cmd = cmd ;
pay - > payment_hash = b11 - > payment_hash ;
if ( b11 - > msatoshi ) {
msatoshi = * b11 - > msatoshi ;
2018-01-15 00:11:41 +01:00
if ( msatoshitok ) {
2017-10-26 05:07:19 +02:00
command_fail ( cmd , " msatoshi parameter unnecessary " ) ;
return ;
}
} else {
if ( ! msatoshitok ) {
command_fail ( cmd , " msatoshi parameter required " ) ;
return ;
}
if ( ! json_tok_u64 ( buffer , msatoshitok , & msatoshi ) ) {
command_fail ( cmd ,
" msatoshi '%.*s' is not a valid number " ,
( int ) ( msatoshitok - > end - msatoshitok - > start ) ,
buffer + msatoshitok - > start ) ;
return ;
}
}
if ( riskfactortok
& & ! json_tok_double ( buffer , riskfactortok , & riskfactor ) ) {
command_fail ( cmd , " '%.*s' is not a valid double " ,
( int ) ( riskfactortok - > end - riskfactortok - > start ) ,
buffer + riskfactortok - > start ) ;
return ;
}
/* FIXME: use b11->routes */
req = towire_gossip_getroute_request ( cmd , & cmd - > ld - > id ,
& b11 - > receiver_id ,
2017-10-26 05:08:19 +02:00
msatoshi , riskfactor * 1000 ,
b11 - > min_final_cltv_expiry ) ;
2017-10-26 05:07:19 +02:00
subd_req ( pay , cmd - > ld - > gossip , req , - 1 , 0 , json_pay_getroute_reply , pay ) ;
2017-12-15 11:15:54 +01:00
command_still_pending ( cmd ) ;
2017-10-26 05:07:19 +02:00
}
static const struct json_command pay_command = {
" pay " ,
json_pay ,
2018-01-13 12:21:33 +01:00
" Send payment specified by {bolt11} with optional {msatoshi} (iff {bolt11} does not have amount), {description} (required if {bolt11} uses description hash) and {riskfactor} (default 1.0) " ,
2017-10-26 05:07:19 +02:00
" Returns the {preimage} on success "
} ;
AUTODATA ( json_command , & pay_command ) ;
2017-11-16 19:09:09 +01:00
static void json_listpayments ( struct command * cmd , const char * buffer ,
const jsmntok_t * params )
{
const struct wallet_payment * * payments ;
struct json_result * response = new_json_result ( cmd ) ;
2018-01-16 20:44:32 +01:00
jsmntok_t * bolt11tok , * rhashtok ;
struct sha256 * rhash = NULL ;
2017-11-16 19:09:09 +01:00
2018-01-16 20:44:32 +01:00
if ( ! json_get_params ( buffer , params ,
" ?bolt11 " , & bolt11tok ,
" ?payment_hash " , & rhashtok ,
NULL ) ) {
command_fail ( cmd , " Invalid parameters " ) ;
return ;
}
if ( bolt11tok ) {
struct bolt11 * b11 ;
char * b11str , * fail ;
if ( rhashtok ) {
command_fail ( cmd , " Can only specify one of "
" {bolt11} or {payment_hash} " ) ;
return ;
}
b11str = tal_strndup ( cmd , buffer + bolt11tok - > start ,
bolt11tok - > end - bolt11tok - > start ) ;
b11 = bolt11_decode ( cmd , b11str , NULL , & fail ) ;
if ( ! b11 ) {
command_fail ( cmd , " Invalid bolt11: %s " , fail ) ;
return ;
}
rhash = & b11 - > payment_hash ;
} else if ( rhashtok ) {
rhash = tal ( cmd , struct sha256 ) ;
if ( ! hex_decode ( buffer + rhashtok - > start ,
rhashtok - > end - rhashtok - > start ,
rhash , sizeof ( * rhash ) ) ) {
command_fail ( cmd , " '%.*s' is not a valid sha256 hash " ,
( int ) ( rhashtok - > end - rhashtok - > start ) ,
buffer + rhashtok - > start ) ;
return ;
}
}
payments = wallet_payment_list ( cmd , cmd - > ld - > wallet , rhash ) ;
2017-11-16 19:09:09 +01:00
2018-01-17 00:53:18 +01:00
json_object_start ( response , NULL ) ;
json_array_start ( response , " payments " ) ;
2017-11-16 19:09:09 +01:00
for ( int i = 0 ; i < tal_count ( payments ) ; i + + ) {
const struct wallet_payment * t = payments [ i ] ;
json_object_start ( response , NULL ) ;
json_add_u64 ( response , " id " , t - > id ) ;
json_add_hex ( response , " payment_hash " , & t - > payment_hash , sizeof ( t - > payment_hash ) ) ;
2018-01-17 21:22:22 +01:00
json_add_pubkey ( response , " destination " , & t - > destination ) ;
json_add_u64 ( response , " msatoshi " , t - > msatoshi ) ;
2018-01-19 10:53:24 +01:00
if ( deprecated_apis )
json_add_u64 ( response , " timestamp " , t - > timestamp ) ;
json_add_u64 ( response , " created_at " , t - > timestamp ) ;
2017-11-16 19:09:09 +01:00
switch ( t - > status ) {
case PAYMENT_PENDING :
json_add_string ( response , " status " , " pending " ) ;
break ;
case PAYMENT_COMPLETE :
json_add_string ( response , " status " , " complete " ) ;
break ;
case PAYMENT_FAILED :
json_add_string ( response , " status " , " failed " ) ;
break ;
}
2018-01-17 21:29:49 +01:00
if ( t - > payment_preimage )
json_add_hex ( response , " payment_preimage " ,
t - > payment_preimage ,
sizeof ( * t - > payment_preimage ) ) ;
2017-11-16 19:09:09 +01:00
json_object_end ( response ) ;
}
json_array_end ( response ) ;
2018-01-17 00:53:18 +01:00
json_object_end ( response ) ;
2017-11-16 19:09:09 +01:00
command_success ( cmd , response ) ;
}
static const struct json_command listpayments_command = {
" listpayments " ,
json_listpayments ,
2018-01-16 20:44:32 +01:00
" Get a list of outgoing payments " ,
2018-01-19 10:53:24 +01:00
" Returns a list of payments with {payment_hash}, {destination}, {msatoshi}, {created_at} and {status} (and {payment_preimage} if {status} is 'complete') "
2017-11-16 19:09:09 +01:00
} ;
AUTODATA ( json_command , & listpayments_command ) ;