2021-12-04 12:23:56 +01:00
# include "config.h"
2020-07-07 22:50:26 +02:00
# include <bitcoin/address.h>
# include <bitcoin/base58.h>
2020-07-07 22:50:21 +02:00
# include <bitcoin/feerate.h>
2020-09-03 19:59:55 +02:00
# include <bitcoin/psbt.h>
2020-07-07 22:50:26 +02:00
# include <bitcoin/script.h>
2022-07-04 05:49:38 +02:00
# include <ccan/asort/asort.h>
2019-06-12 02:38:54 +02:00
# include <ccan/json_escape/json_escape.h>
2022-07-04 05:49:38 +02:00
# include <ccan/mem/mem.h>
2018-12-08 01:39:28 +01:00
# include <ccan/str/hex/hex.h>
# include <ccan/tal/str/str.h>
2020-07-07 22:50:26 +02:00
# include <common/bech32.h>
2022-07-04 05:49:38 +02:00
# include <common/configdir.h>
2018-12-08 01:39:28 +01:00
# include <common/json_command.h>
2022-07-04 05:49:38 +02:00
# include <common/json_param.h>
2021-11-04 22:06:01 +01:00
# include <common/route.h>
2018-12-08 01:39:28 +01:00
2022-07-04 05:49:38 +02:00
struct param {
const char * name ;
bool is_set ;
enum param_style style ;
param_cbx cbx ;
void * arg ;
} ;
static bool param_add ( struct param * * params ,
const char * name ,
enum param_style style ,
param_cbx cbx , void * arg )
{
# if DEVELOPER
if ( ! ( name & & cbx & & arg ) )
return false ;
# endif
struct param last ;
last . is_set = false ;
last . name = name ;
last . style = style ;
last . cbx = cbx ;
last . arg = arg ;
tal_arr_expand ( params , last ) ;
return true ;
}
/* FIXME: To support the deprecated p_req_dup_ok */
static bool is_required ( enum param_style style )
{
return style = = PARAM_REQUIRED | | style = = PARAM_REQUIRED_ALLOW_DUPS ;
}
static struct command_result * make_callback ( struct command * cmd ,
struct param * def ,
const char * buffer ,
const jsmntok_t * tok )
{
/* If it had a default, free that now to avoid leak */
if ( def - > style = = PARAM_OPTIONAL_WITH_DEFAULT & & ! def - > is_set )
tal_free ( * ( void * * ) def - > arg ) ;
def - > is_set = true ;
return def - > cbx ( cmd , def - > name , buffer , tok , def - > arg ) ;
}
static struct command_result * post_check ( struct command * cmd ,
struct param * params )
{
struct param * first = params ;
struct param * last = first + tal_count ( params ) ;
/* Make sure required params were provided. */
while ( first ! = last & & is_required ( first - > style ) ) {
if ( ! first - > is_set ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" missing required parameter: %s " ,
first - > name ) ;
}
first + + ;
}
return NULL ;
}
static struct command_result * parse_by_position ( struct command * cmd ,
struct param * params ,
const char * buffer ,
const jsmntok_t tokens [ ] ,
bool allow_extra )
{
struct command_result * res ;
const jsmntok_t * tok ;
size_t i ;
json_for_each_arr ( i , tok , tokens ) {
/* check for unexpected trailing params */
if ( i = = tal_count ( params ) ) {
if ( ! allow_extra ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" too many parameters: "
" got %u, expected %zu " ,
tokens - > size ,
tal_count ( params ) ) ;
}
break ;
}
if ( ! json_tok_is_null ( buffer , tok ) ) {
res = make_callback ( cmd , params + i , buffer , tok ) ;
if ( res )
return res ;
}
}
return post_check ( cmd , params ) ;
}
static struct param * find_param ( struct param * params , const char * start ,
size_t n )
{
struct param * first = params ;
struct param * last = first + tal_count ( params ) ;
while ( first ! = last ) {
size_t arglen = strcspn ( first - > name , " | " ) ;
if ( memeq ( first - > name , arglen , start , n ) )
return first ;
if ( deprecated_apis
& & first - > name [ arglen ]
& & memeq ( first - > name + arglen + 1 ,
strlen ( first - > name + arglen + 1 ) ,
start , n ) )
return first ;
first + + ;
}
return NULL ;
}
static struct command_result * parse_by_name ( struct command * cmd ,
struct param * params ,
const char * buffer ,
const jsmntok_t tokens [ ] ,
bool allow_extra )
{
size_t i ;
const jsmntok_t * t ;
json_for_each_obj ( i , t , tokens ) {
struct param * p = find_param ( params , buffer + t - > start ,
t - > end - t - > start ) ;
if ( ! p ) {
if ( ! allow_extra ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" unknown parameter: %.*s, this may be caused by a failure to autodetect key=value-style parameters. Please try using the -k flag and explicit key=value pairs of parameters. " ,
t - > end - t - > start ,
buffer + t - > start ) ;
}
} else {
struct command_result * res ;
if ( p - > is_set ) {
if ( p - > style = = PARAM_REQUIRED_ALLOW_DUPS )
continue ;
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" duplicate json names: %s " ,
p - > name ) ;
}
res = make_callback ( cmd , p , buffer , t + 1 ) ;
if ( res )
return res ;
}
}
return post_check ( cmd , params ) ;
}
# if DEVELOPER
static int comp_by_name ( const struct param * a , const struct param * b ,
void * unused )
{
return strcmp ( a - > name , b - > name ) ;
}
static int comp_by_arg ( const struct param * a , const struct param * b ,
void * unused )
{
/* size_t could be larger than int: don't turn a 4bn difference into 0 */
if ( a - > arg > b - > arg )
return 1 ;
else if ( a - > arg < b - > arg )
return - 1 ;
return 0 ;
}
/* This comparator is a bit different, but works well.
* Return 0 if @ a is optional and @ b is required . Otherwise return 1.
*/
static int comp_req_order ( const struct param * a , const struct param * b ,
void * unused )
{
if ( ! is_required ( a - > style ) & & is_required ( b - > style ) )
return 0 ;
return 1 ;
}
/*
* Make sure 2 sequential items in @ params are not equal ( based on
* provided comparator ) .
*/
static bool check_distinct ( struct param * params ,
int ( * compar ) ( const struct param * a ,
const struct param * b , void * unused ) )
{
struct param * first = params ;
struct param * last = first + tal_count ( params ) ;
first + + ;
while ( first ! = last ) {
if ( compar ( first - 1 , first , NULL ) = = 0 )
return false ;
first + + ;
}
return true ;
}
static bool check_unique ( struct param * copy ,
int ( * compar ) ( const struct param * a ,
const struct param * b , void * unused ) )
{
asort ( copy , tal_count ( copy ) , compar , NULL ) ;
return check_distinct ( copy , compar ) ;
}
/*
* Verify consistent internal state .
*/
static bool check_params ( struct param * params )
{
if ( tal_count ( params ) < 2 )
return true ;
/* make sure there are no required params following optional */
if ( ! check_distinct ( params , comp_req_order ) )
return false ;
/* duplicate so we can sort */
struct param * copy = tal_dup_talarr ( params , struct param , params ) ;
/* check for repeated names and args */
if ( ! check_unique ( copy , comp_by_name ) )
return false ;
if ( ! check_unique ( copy , comp_by_arg ) )
return false ;
tal_free ( copy ) ;
return true ;
}
# endif
static char * param_usage ( const tal_t * ctx ,
const struct param * params )
{
char * usage = tal_strdup ( ctx , " " ) ;
for ( size_t i = 0 ; i < tal_count ( params ) ; i + + ) {
/* Don't print |deprecated part! */
int len = strcspn ( params [ i ] . name , " | " ) ;
if ( i ! = 0 )
tal_append_fmt ( & usage , " " ) ;
if ( is_required ( params [ i ] . style ) )
tal_append_fmt ( & usage , " %.*s " , len , params [ i ] . name ) ;
else
tal_append_fmt ( & usage , " [%.*s] " , len , params [ i ] . name ) ;
}
return usage ;
}
static struct command_result * param_arr ( struct command * cmd , const char * buffer ,
const jsmntok_t tokens [ ] ,
struct param * params ,
bool allow_extra )
{
# if DEVELOPER
if ( ! check_params ( params ) ) {
return command_fail ( cmd , PARAM_DEV_ERROR ,
" developer error: check_params " ) ;
}
# endif
if ( tokens - > type = = JSMN_ARRAY )
return parse_by_position ( cmd , params , buffer , tokens , allow_extra ) ;
else if ( tokens - > type = = JSMN_OBJECT )
return parse_by_name ( cmd , params , buffer , tokens , allow_extra ) ;
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Expected array or object for params " ) ;
}
const char * param_subcommand ( struct command * cmd , const char * buffer ,
const jsmntok_t tokens [ ] ,
const char * name , . . . )
{
va_list ap ;
struct param * params = tal_arr ( cmd , struct param , 0 ) ;
const char * arg , * * names = tal_arr ( tmpctx , const char * , 1 ) ;
const char * subcmd ;
param_add ( & params , " subcommand " , PARAM_REQUIRED , ( void * ) param_string , & subcmd ) ;
names [ 0 ] = name ;
va_start ( ap , name ) ;
while ( ( arg = va_arg ( ap , const char * ) ) ! = NULL )
tal_arr_expand ( & names , arg ) ;
va_end ( ap ) ;
if ( command_usage_only ( cmd ) ) {
char * usage = tal_strdup ( cmd , " subcommand " ) ;
for ( size_t i = 0 ; i < tal_count ( names ) ; i + + )
tal_append_fmt ( & usage , " %c%s " ,
i = = 0 ? ' = ' : ' | ' , names [ i ] ) ;
command_set_usage ( cmd , usage ) ;
return NULL ;
}
/* Check it's valid */
if ( param_arr ( cmd , buffer , tokens , params , true ) ! = NULL ) {
return NULL ;
}
/* Check it's one of the known ones. */
for ( size_t i = 0 ; i < tal_count ( names ) ; i + + )
if ( streq ( subcmd , names [ i ] ) )
return subcmd ;
/* We really do ignore this. */
struct command_result * ignore ;
ignore = command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Unknown subcommand '%s' " , subcmd ) ;
assert ( ignore ) ;
return NULL ;
}
bool param ( struct command * cmd , const char * buffer ,
const jsmntok_t tokens [ ] , . . . )
{
struct param * params = tal_arr ( tmpctx , struct param , 0 ) ;
const char * name ;
va_list ap ;
bool allow_extra = false ;
va_start ( ap , tokens ) ;
while ( ( name = va_arg ( ap , const char * ) ) ! = NULL ) {
enum param_style style = va_arg ( ap , enum param_style ) ;
param_cbx cbx = va_arg ( ap , param_cbx ) ;
void * arg = va_arg ( ap , void * ) ;
if ( streq ( name , " " ) ) {
allow_extra = true ;
continue ;
}
if ( ! param_add ( & params , name , style , cbx , arg ) ) {
/* We really do ignore this return! */
struct command_result * ignore ;
ignore = command_fail ( cmd , PARAM_DEV_ERROR ,
" developer error: param_add %s " , name ) ;
assert ( ignore ) ;
va_end ( ap ) ;
return false ;
}
}
va_end ( ap ) ;
if ( command_usage_only ( cmd ) ) {
command_set_usage ( cmd , param_usage ( cmd , params ) ) ;
return false ;
}
/* Always return false if we're simply checking command parameters;
* normally this returns true if all parameters are valid . */
return param_arr ( cmd , buffer , tokens , params , allow_extra ) = = NULL
& & ! command_check_only ( cmd ) ;
}
2018-12-16 05:50:06 +01:00
struct command_result * param_array ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
const jsmntok_t * * arr )
2018-12-08 01:39:28 +01:00
{
2018-12-16 05:50:06 +01:00
if ( tok - > type = = JSMN_ARRAY ) {
* arr = tok ;
return NULL ;
}
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok , " should be an array " ) ;
2018-12-08 01:39:28 +01:00
}
2018-12-16 05:50:06 +01:00
struct command_result * param_bool ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
bool * * b )
2018-12-08 01:39:28 +01:00
{
* b = tal ( cmd , bool ) ;
2018-12-16 05:47:06 +01:00
if ( json_to_bool ( buffer , tok , * b ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be 'true' or 'false' " ) ;
2018-12-08 01:39:28 +01:00
}
2020-01-29 12:30:00 +01:00
struct command_result * param_millionths ( struct command * cmd , const char * name ,
const char * buffer ,
const jsmntok_t * tok , uint64_t * * num )
2018-12-08 01:39:28 +01:00
{
2020-02-19 11:11:36 +01:00
* num = tal ( cmd , uint64_t ) ;
if ( json_to_millionths ( buffer , tok , * num ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a non-negative floating-point number " ) ;
2018-12-08 01:39:28 +01:00
}
2018-12-16 05:50:06 +01:00
struct command_result * param_escaped_string ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
const char * * str )
2018-12-08 01:39:28 +01:00
{
2019-06-12 02:38:54 +02:00
if ( tok - > type = = JSMN_STRING ) {
struct json_escape * esc ;
/* jsmn always gives us ~ well-formed strings. */
esc = json_escape_string_ ( cmd , buffer + tok - > start ,
tok - > end - tok - > start ) ;
* str = json_escape_unescape ( cmd , esc ) ;
2018-12-08 01:39:28 +01:00
if ( * str )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
}
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a string (without \\ u) " ) ;
2018-12-08 01:39:28 +01:00
}
2018-12-16 05:50:06 +01:00
struct command_result * param_string ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
const char * * str )
2018-12-08 01:39:28 +01:00
{
* str = tal_strndup ( cmd , buffer + tok - > start ,
tok - > end - tok - > start ) ;
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
}
2019-07-16 03:41:51 +02:00
struct command_result * param_ignore ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
const void * unused )
{
return NULL ;
}
2018-12-16 05:50:06 +01:00
struct command_result * param_label ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
2019-06-12 02:38:54 +02:00
struct json_escape * * label )
2018-12-08 01:39:28 +01:00
{
/* We accept both strings and number literals here. */
2019-06-12 02:38:54 +02:00
* label = json_escape_string_ ( cmd , buffer + tok - > start , tok - > end - tok - > start ) ;
2018-12-08 01:39:28 +01:00
if ( * label & & ( tok - > type = = JSMN_STRING | | json_tok_is_num ( buffer , tok ) ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a string or number " ) ;
2018-12-08 01:39:28 +01:00
}
2018-12-16 05:50:06 +01:00
struct command_result * param_number ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
unsigned int * * num )
2018-12-08 01:39:28 +01:00
{
* num = tal ( cmd , unsigned int ) ;
if ( json_to_number ( buffer , tok , * num ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an integer " ) ;
2018-12-08 01:39:28 +01:00
}
2018-12-16 05:50:06 +01:00
struct command_result * param_sha256 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct sha256 * * hash )
2018-12-08 01:39:28 +01:00
{
* hash = tal ( cmd , struct sha256 ) ;
if ( hex_decode ( buffer + tok - > start ,
tok - > end - tok - > start ,
* hash , sizeof ( * * hash ) ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a 32 byte hex value " ) ;
2018-12-08 01:39:28 +01:00
}
2022-05-19 13:51:49 +02:00
struct command_result * param_u32 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
uint32_t * * num )
{
* num = tal ( cmd , uint32_t ) ;
if ( json_to_u32 ( buffer , tok , * num ) )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an unsigned 32 bit integer " ) ;
}
2018-12-16 05:50:06 +01:00
struct command_result * param_u64 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
uint64_t * * num )
2018-12-08 01:39:28 +01:00
{
* num = tal ( cmd , uint64_t ) ;
if ( json_to_u64 ( buffer , tok , * num ) )
2018-12-16 05:50:06 +01:00
return NULL ;
2018-12-08 01:39:28 +01:00
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an unsigned 64 bit integer " ) ;
2018-12-08 01:39:28 +01:00
}
2019-02-21 01:45:57 +01:00
struct command_result * param_msat ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct amount_msat * * msat )
{
* msat = tal ( cmd , struct amount_msat ) ;
if ( parse_amount_msat ( * msat , buffer + tok - > start , tok - > end - tok - > start ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a millisatoshi amount " ) ;
2019-02-21 01:45:57 +01:00
}
struct command_result * param_sat ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct amount_sat * * sat )
{
* sat = tal ( cmd , struct amount_sat ) ;
if ( parse_amount_sat ( * sat , buffer + tok - > start , tok - > end - tok - > start ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a satoshi amount " ) ;
2019-02-21 01:45:57 +01:00
}
2019-08-15 19:41:23 +02:00
struct command_result * param_sat_or_all ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct amount_sat * * sat )
{
if ( json_tok_streq ( buffer , tok , " all " ) ) {
* sat = tal ( cmd , struct amount_sat ) ;
* * sat = AMOUNT_SAT ( - 1ULL ) ;
return NULL ;
}
return param_sat ( cmd , name , buffer , tok , sat ) ;
}
2019-08-28 05:23:14 +02:00
struct command_result * param_node_id ( struct command * cmd , const char * name ,
2020-02-04 01:03:22 +01:00
const char * buffer , const jsmntok_t * tok ,
2019-08-28 05:23:14 +02:00
struct node_id * * id )
{
* id = tal ( cmd , struct node_id ) ;
if ( json_to_node_id ( buffer , tok , * id ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a node id " ) ;
2019-08-28 05:23:14 +02:00
}
2019-09-30 18:31:27 +02:00
struct command_result * param_channel_id ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct channel_id * * cid )
{
* cid = tal ( cmd , struct channel_id ) ;
if ( json_to_channel_id ( buffer , tok , * cid ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a channel id " ) ;
2019-09-30 18:31:27 +02:00
}
2019-11-07 17:56:06 +01:00
2021-05-22 07:00:22 +02:00
struct command_result * param_short_channel_id ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct short_channel_id * * scid )
{
* scid = tal ( cmd , struct short_channel_id ) ;
if ( json_to_short_channel_id ( buffer , tok , * scid ) )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a short_channel_id of form NxNxN " ) ;
}
2019-11-07 17:56:06 +01:00
struct command_result * param_secret ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct secret * * secret )
{
* secret = tal ( cmd , struct secret ) ;
if ( hex_decode ( buffer + tok - > start ,
tok - > end - tok - > start ,
* secret , sizeof ( * * secret ) ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a 32 byte hex value " ) ;
2019-11-07 17:56:06 +01:00
}
struct command_result * param_bin_from_hex ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
u8 * * bin )
{
* bin = json_tok_bin_from_hex ( cmd , buffer , tok ) ;
if ( bin ! = NULL )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a hex value " ) ;
2019-11-07 17:56:06 +01:00
}
2019-11-24 19:09:19 +01:00
struct command_result * param_hops_array ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct sphinx_hop * * hops )
{
2019-12-05 11:06:28 +01:00
const jsmntok_t * hop , * payloadtok , * pubkeytok ;
2019-11-24 19:09:19 +01:00
struct sphinx_hop h ;
size_t i ;
if ( tok - > type ! = JSMN_ARRAY ) {
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an array of hops " ) ;
2019-11-24 19:09:19 +01:00
}
* hops = tal_arr ( cmd , struct sphinx_hop , 0 ) ;
json_for_each_arr ( i , hop , tok ) {
payloadtok = json_get_member ( buffer , hop , " payload " ) ;
pubkeytok = json_get_member ( buffer , hop , " pubkey " ) ;
if ( ! pubkeytok )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Hop %zu does not have a pubkey " , i ) ;
if ( ! payloadtok )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Hop %zu does not have a payload " , i ) ;
2019-12-05 11:06:28 +01:00
h . raw_payload = json_tok_bin_from_hex ( * hops , buffer , payloadtok ) ;
2019-11-24 19:09:19 +01:00
if ( ! json_to_pubkey ( buffer , pubkeytok , & h . pubkey ) )
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , pubkeytok ,
" should be a pubkey " ) ;
2019-11-24 19:09:19 +01:00
2019-12-05 11:06:28 +01:00
if ( ! h . raw_payload )
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer ,
payloadtok ,
" should be hex " ) ;
2019-11-24 19:09:19 +01:00
tal_arr_expand ( hops , h ) ;
}
if ( tal_count ( * hops ) = = 0 ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" At least one hop must be specified. " ) ;
}
return NULL ;
}
2019-11-25 13:42:23 +01:00
struct command_result * param_secrets_array ( struct command * cmd ,
const char * name , const char * buffer ,
const jsmntok_t * tok ,
struct secret * * secrets )
{
size_t i ;
const jsmntok_t * s ;
struct secret secret ;
if ( tok - > type ! = JSMN_ARRAY ) {
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an array of secrets " ) ;
2019-11-25 13:42:23 +01:00
}
* secrets = tal_arr ( cmd , struct secret , 0 ) ;
json_for_each_arr ( i , s , tok ) {
if ( ! hex_decode ( buffer + s - > start , s - > end - s - > start , & secret ,
sizeof ( secret ) ) )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" '%s[%zu]' should be a 32 byte hex "
" value, not '%.*s' " ,
name , i , s - > end - s - > start ,
buffer + s - > start ) ;
tal_arr_expand ( secrets , secret ) ;
}
return NULL ;
}
2020-07-07 22:50:21 +02:00
2020-07-07 22:50:26 +02:00
/**
* segwit_addr_net_decode - Try to decode a Bech32 address and detect
* testnet / mainnet / regtest / signet
*
* This processes the address and returns a string if it is a Bech32
* address specified by BIP173 . The string is set whether it is
2020-09-22 10:04:05 +02:00
* testnet or signet ( both " tb " ) , mainnet ( " bc " ) , regtest ( " bcrt " )
2020-07-07 22:50:26 +02:00
* It does not check , witness version and program size restrictions .
*
* Out : 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 string containing the human readable segment of bech32 address
*/
static const char * segwit_addr_net_decode ( int * witness_version ,
uint8_t * witness_program ,
size_t * witness_program_len ,
const char * addrz ,
const struct chainparams * chainparams )
{
if ( segwit_addr_decode ( witness_version , witness_program ,
2021-11-29 08:10:06 +01:00
witness_program_len , chainparams - > onchain_hrp ,
2020-07-07 22:50:26 +02:00
addrz ) )
2021-11-29 08:10:06 +01:00
return chainparams - > onchain_hrp ;
2020-07-07 22:50:26 +02:00
else
return NULL ;
}
enum address_parse_result
json_to_address_scriptpubkey ( const tal_t * ctx ,
const struct chainparams * chainparams ,
const char * buffer ,
const jsmntok_t * tok , const u8 * * scriptpubkey )
{
struct bitcoin_address 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 ;
char * addrz ;
const char * bip173 ;
bool parsed ;
bool right_network ;
u8 addr_version ;
parsed =
ripemd160_from_base58 ( & addr_version , & destination . addr ,
buffer + tok - > start , tok - > end - tok - > start ) ;
if ( parsed ) {
if ( addr_version = = chainparams - > p2pkh_version ) {
* scriptpubkey = scriptpubkey_p2pkh ( ctx , & destination ) ;
return ADDRESS_PARSE_SUCCESS ;
} else if ( addr_version = = chainparams - > p2sh_version ) {
* scriptpubkey =
scriptpubkey_p2sh_hash ( ctx , & destination . addr ) ;
return ADDRESS_PARSE_SUCCESS ;
} else {
return ADDRESS_PARSE_WRONG_NETWORK ;
}
/* Insert other parsers that accept pointer+len here. */
}
/* Generate null-terminated address. */
addrz = tal_dup_arr ( ctx , char , buffer + tok - > start , tok - > end - tok - > start , 1 ) ;
addrz [ tok - > end - tok - > start ] = ' \0 ' ;
bip173 = segwit_addr_net_decode ( & witness_version , witness_program ,
& witness_program_len , addrz , chainparams ) ;
if ( bip173 ) {
2021-06-09 05:10:00 +02:00
bool witness_ok ;
/* We know the rules for v0, rest remain undefined */
if ( witness_version = = 0 ) {
witness_ok = ( witness_program_len = = 20 | |
witness_program_len = = 32 ) ;
} else
2020-07-07 22:50:26 +02:00
witness_ok = true ;
if ( witness_ok ) {
* scriptpubkey = scriptpubkey_witness_raw ( ctx , witness_version ,
witness_program , witness_program_len ) ;
parsed = true ;
2021-11-29 08:10:06 +01:00
right_network = streq ( bip173 , chainparams - > onchain_hrp ) ;
2020-07-07 22:50:26 +02:00
}
}
/* Insert other parsers that accept null-terminated string here. */
tal_free ( addrz ) ;
if ( parsed ) {
if ( right_network )
return ADDRESS_PARSE_SUCCESS ;
else
return ADDRESS_PARSE_WRONG_NETWORK ;
}
return ADDRESS_PARSE_UNRECOGNIZED ;
}
2020-07-07 22:50:28 +02:00
struct command_result * param_txid ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct bitcoin_txid * * txid )
{
* txid = tal ( cmd , struct bitcoin_txid ) ;
if ( json_to_txid ( buffer , tok , * txid ) )
return NULL ;
2020-08-25 23:20:50 +02:00
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a txid " ) ;
2020-07-07 22:50:28 +02:00
}
2020-08-07 03:21:33 +02:00
struct command_result * param_bitcoin_address ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
const u8 * * scriptpubkey )
{
/* Parse address. */
switch ( json_to_address_scriptpubkey ( cmd ,
chainparams ,
buffer , tok ,
scriptpubkey ) ) {
case ADDRESS_PARSE_UNRECOGNIZED :
return command_fail ( cmd , LIGHTNINGD ,
" Could not parse destination address, "
" %s should be a valid address " ,
name ? name : " address field " ) ;
case ADDRESS_PARSE_WRONG_NETWORK :
return command_fail ( cmd , LIGHTNINGD ,
" Destination address is not on network %s " ,
chainparams - > network_name ) ;
case ADDRESS_PARSE_SUCCESS :
return NULL ;
}
abort ( ) ;
}
2020-09-03 19:59:55 +02:00
struct command_result * param_psbt ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct wally_psbt * * psbt )
{
* psbt = psbt_from_b64 ( cmd , buffer + tok - > start , tok - > end - tok - > start ) ;
if ( * psbt )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" Expected a PSBT " ) ;
}
2020-12-04 11:24:14 +01:00
struct command_result * param_outpoint_arr ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct bitcoin_outpoint * * outpoints )
{
size_t i ;
const jsmntok_t * curr ;
if ( tok - > type ! = JSMN_ARRAY ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode the outpoint array for %s: "
" \" %s \" is not a valid outpoint array. " ,
name , json_strdup ( tmpctx , buffer , tok ) ) ;
}
* outpoints = tal_arr ( cmd , struct bitcoin_outpoint , tok - > size ) ;
json_for_each_arr ( i , curr , tok ) {
2020-12-08 01:24:34 +01:00
if ( ! json_to_outpoint ( buffer , curr , & ( * outpoints ) [ i ] ) )
2020-12-04 11:24:14 +01:00
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode outpoint \" %.*s \" , "
" expected format: txid:output " ,
json_tok_full_len ( curr ) , json_tok_full ( buffer , curr ) ) ;
}
return NULL ;
}
2021-06-17 18:06:43 +02:00
struct command_result * param_extra_tlvs ( struct command * cmd , const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct tlv_field * * fields )
{
size_t i ;
const jsmntok_t * curr ;
struct tlv_field * f , * temp ;
if ( tok - > type ! = JSMN_OBJECT ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode the TLV object from %s: "
" \" %s \" is not a valid JSON object. " ,
name , json_strdup ( tmpctx , buffer , tok ) ) ;
}
temp = tal_arr ( cmd , struct tlv_field , tok - > size ) ;
json_for_each_obj ( i , curr , tok ) {
f = & temp [ i ] ;
if ( ! json_to_u64 ( buffer , curr , & f - > numtype ) ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" \" %s \" is not a valid numeric TLV type. " ,
json_strdup ( tmpctx , buffer , curr ) ) ;
}
f - > value = json_tok_bin_from_hex ( temp , buffer , curr + 1 ) ;
if ( f - > value = = NULL ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" \" %s \" is not a valid hex encoded TLV value. " ,
json_strdup ( tmpctx , buffer , curr ) ) ;
}
f - > length = tal_bytelen ( f - > value ) ;
f - > meta = NULL ;
}
* fields = temp ;
return NULL ;
}
2021-06-18 17:21:41 +02:00
2021-12-04 12:26:06 +01:00
static struct command_result * param_routehint ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct route_info * * ri )
2021-06-18 17:21:41 +02:00
{
size_t i ;
const jsmntok_t * curr ;
const char * err ;
if ( tok - > type ! = JSMN_ARRAY ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" Routehint %s ( \" %s \" ) is not an array of hop objects " ,
name , json_strdup ( tmpctx , buffer , tok ) ) ;
}
* ri = tal_arr ( cmd , struct route_info , tok - > size ) ;
json_for_each_arr ( i , curr , tok ) {
struct route_info * e = & ( * ri ) [ i ] ;
struct amount_msat temp ;
err = json_scan ( tmpctx , buffer , curr ,
" {id:%,scid:%,feebase:%,feeprop:%,expirydelta:%} " ,
JSON_SCAN ( json_to_node_id , & e - > pubkey ) ,
JSON_SCAN ( json_to_short_channel_id , & e - > short_channel_id ) ,
JSON_SCAN ( json_to_msat , & temp ) ,
JSON_SCAN ( json_to_u32 , & e - > fee_proportional_millionths ) ,
JSON_SCAN ( json_to_u16 , & e - > cltv_expiry_delta )
) ;
e - > fee_base_msat =
temp . millisatoshis ; /* Raw: internal conversion. */
if ( err ! = NULL ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" Error parsing routehint %s[%zu]: %s " , name , i ,
err ) ;
}
}
return NULL ;
}
struct command_result *
param_routehint_array ( struct command * cmd , const char * name , const char * buffer ,
const jsmntok_t * tok , struct route_info * * * ris )
{
size_t i ;
const jsmntok_t * curr ;
char * element_name ;
struct command_result * err ;
if ( tok - > type ! = JSMN_ARRAY ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" Routehint array %s ( \" %s \" ) is not an array " ,
name , json_strdup ( tmpctx , buffer , tok ) ) ;
}
* ris = tal_arr ( cmd , struct route_info * , 0 ) ;
json_for_each_arr ( i , curr , tok ) {
struct route_info * element ;
element_name = tal_fmt ( cmd , " %s[%zu] " , name , i ) ;
err = param_routehint ( cmd , element_name , buffer , curr , & element ) ;
if ( err ! = NULL ) {
return err ;
}
tal_arr_expand ( ris , element ) ;
tal_free ( element_name ) ;
}
return NULL ;
}
2021-07-02 23:00:17 +02:00
2021-11-04 22:06:01 +01:00
struct command_result * param_route_exclusion ( struct command * cmd ,
const char * name , const char * buffer , const jsmntok_t * tok ,
struct route_exclusion * * re )
{
* re = tal ( cmd , struct route_exclusion ) ;
struct short_channel_id_dir * chan_id =
tal ( tmpctx , struct short_channel_id_dir ) ;
if ( ! short_channel_id_dir_from_str ( buffer + tok - > start ,
tok - > end - tok - > start ,
chan_id ) ) {
struct node_id * node_id = tal ( tmpctx , struct node_id ) ;
if ( ! json_to_node_id ( buffer , tok , node_id ) )
return command_fail_badparam ( cmd , " exclude " ,
buffer , tok ,
" should be short_channel_id_dir or node_id " ) ;
( * re ) - > type = EXCLUDE_NODE ;
( * re ) - > u . node_id = * node_id ;
} else {
( * re ) - > type = EXCLUDE_CHANNEL ;
( * re ) - > u . chan_id = * chan_id ;
}
return NULL ;
}
struct command_result *
param_route_exclusion_array ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct route_exclusion * * * res )
{
size_t i ;
const jsmntok_t * curr ;
char * element_name ;
struct command_result * err ;
if ( tok - > type ! = JSMN_ARRAY ) {
return command_fail (
cmd , JSONRPC2_INVALID_PARAMS ,
" Exclude array %s ( \" %s \" ) is not an array " ,
name , json_strdup ( tmpctx , buffer , tok ) ) ;
}
* res = tal_arr ( cmd , struct route_exclusion * , 0 ) ;
json_for_each_arr ( i , curr , tok ) {
struct route_exclusion * element ;
element_name = tal_fmt ( cmd , " %s[%zu] " , name , i ) ;
err = param_route_exclusion ( cmd , element_name , buffer , curr , & element ) ;
if ( err ! = NULL ) {
return err ;
}
tal_arr_expand ( res , element ) ;
tal_free ( element_name ) ;
}
return NULL ;
}
2021-07-02 23:00:17 +02:00
struct command_result * param_lease_hex ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct lease_rates * * rates )
{
2021-07-11 08:59:42 +02:00
* rates = lease_rates_fromhex ( cmd , buffer + tok - > start ,
tok - > end - tok - > start ) ;
if ( ! * rates )
2021-07-02 23:00:17 +02:00
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode '%s' %.*s " ,
name , json_tok_full_len ( tok ) ,
json_tok_full ( buffer , tok ) ) ;
return NULL ;
}
2022-07-04 05:52:34 +02:00
struct command_result * param_pubkey ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct pubkey * * pubkey )
{
* pubkey = tal ( cmd , struct pubkey ) ;
if ( json_to_pubkey ( buffer , tok , * pubkey ) )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a compressed pubkey " ) ;
}