2021-12-04 21:53:56 +10:30
# include "config.h"
2020-07-08 06:20:26 +09:30
# include <bitcoin/address.h>
# include <bitcoin/base58.h>
2020-07-08 06:20:21 +09:30
# include <bitcoin/feerate.h>
2020-09-03 12:59:55 -05:00
# include <bitcoin/psbt.h>
2020-07-08 06:20:26 +09:30
# include <bitcoin/script.h>
2022-07-04 13:19:38 +09:30
# include <ccan/asort/asort.h>
2019-06-12 10:08:54 +09:30
# include <ccan/json_escape/json_escape.h>
2022-07-04 13:19:38 +09:30
# include <ccan/mem/mem.h>
2018-12-08 11:09:28 +10:30
# include <ccan/str/hex/hex.h>
# include <ccan/tal/str/str.h>
2020-07-08 06:20:26 +09:30
# include <common/bech32.h>
2022-07-04 13:19:38 +09:30
# include <common/configdir.h>
2018-12-08 11:09:28 +10:30
# include <common/json_command.h>
2022-07-04 13:19:38 +09:30
# include <common/json_param.h>
2021-11-04 17:06:01 -04:00
# include <common/route.h>
2018-12-08 11:09:28 +10:30
2023-07-25 11:09:43 +09:30
/* Overridden by run-param.c */
# ifndef paramcheck_assert
# define paramcheck_assert assert
# endif
2023-09-21 15:06:26 +09:30
/* Overridden by run-param.c */
# ifndef paramcheck_assert
# define paramcheck_assert assert
# endif
2022-07-04 13:19:38 +09:30
struct param {
const char * name ;
2024-01-25 10:58:55 +10:30
const char * depr_start , * depr_end ;
2022-07-04 13:19:38 +09:30
bool is_set ;
enum param_style style ;
param_cbx cbx ;
void * arg ;
} ;
2023-07-25 11:09:43 +09:30
static void param_add ( struct param * * params ,
2022-07-04 13:19:38 +09:30
const char * name ,
enum param_style style ,
2024-01-25 10:58:55 +10:30
const char * depr_start ,
const char * depr_end ,
2022-07-04 13:19:38 +09:30
param_cbx cbx , void * arg )
{
struct param last ;
2023-07-25 11:09:43 +09:30
paramcheck_assert ( name ) ;
paramcheck_assert ( cbx ) ;
paramcheck_assert ( arg ) ;
2022-07-04 13:19:38 +09:30
last . is_set = false ;
last . name = name ;
2024-01-25 10:58:55 +10:30
last . depr_start = depr_start ;
last . depr_end = depr_end ;
2022-07-04 13:19:38 +09:30
last . style = style ;
last . cbx = cbx ;
last . arg = arg ;
tal_arr_expand ( params , last ) ;
}
static bool is_required ( enum param_style style )
{
2024-01-25 10:58:52 +10:30
return style = = PARAM_REQUIRED ;
2022-07-04 13:19:38 +09:30
}
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 */
2023-09-21 15:06:27 +09:30
if ( ( def - > style = = PARAM_OPTIONAL_WITH_DEFAULT
| | def - > style = = PARAM_OPTIONAL_DEV_WITH_DEFAULT )
& & ! def - > is_set )
2022-07-04 13:19:38 +09:30
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 ) ) {
2023-09-21 15:06:27 +09:30
if ( params [ i ] . style = = PARAM_OPTIONAL_DEV_WITH_DEFAULT
& & ! command_dev_apis ( cmd ) ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Parameter %zu is developer-only " , i ) ;
}
2022-07-04 13:19:38 +09:30
res = make_callback ( cmd , params + i , buffer , tok ) ;
if ( res )
return res ;
}
}
return post_check ( cmd , params ) ;
}
2023-07-06 17:06:50 +09:30
static struct param * find_param ( struct command * cmd ,
struct param * params , const char * start ,
2022-07-04 13:19:38 +09:30
size_t n )
{
struct param * first = params ;
struct param * last = first + tal_count ( params ) ;
while ( first ! = last ) {
2024-01-25 10:58:55 +10:30
if ( memeqstr ( start , n , first - > name ) ) {
if ( ! command_deprecated_in_ok ( cmd , first - > name ,
first - > depr_start ,
first - > depr_end ) ) {
return NULL ;
}
2022-07-04 13:19:38 +09:30
return first ;
2024-01-25 10:58:55 +10:30
}
2022-07-04 13:19:38 +09:30
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 ) {
2023-07-06 17:06:50 +09:30
struct param * p = find_param ( cmd , params , buffer + t - > start ,
2022-07-04 13:19:38 +09:30
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 ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" duplicate json names: %s " ,
p - > name ) ;
}
2023-09-21 15:06:27 +09:30
if ( p - > style = = PARAM_OPTIONAL_DEV_WITH_DEFAULT
& & ! command_dev_apis ( cmd ) ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Parameter '%s' is developer-only " ,
p - > name ) ;
}
2022-07-04 13:19:38 +09:30
res = make_callback ( cmd , p , buffer , t + 1 ) ;
if ( res )
return res ;
}
}
return post_check ( cmd , params ) ;
}
static int comp_by_name ( const struct param * a , const struct param * b ,
void * unused )
{
return strcmp ( a - > name , b - > name ) ;
}
/* 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 ) .
*/
2023-07-25 11:09:43 +09:30
static void check_distinct ( const struct param * params ,
int ( * compar ) ( const struct param * a ,
const struct param * b , void * unused ) )
2022-07-04 13:19:38 +09:30
{
2023-07-25 11:09:43 +09:30
const struct param * first = params ;
const struct param * last = first + tal_count ( params ) ;
2022-07-04 13:19:38 +09:30
first + + ;
while ( first ! = last ) {
2023-07-25 11:09:43 +09:30
paramcheck_assert ( compar ( first - 1 , first , NULL ) ! = 0 ) ;
2022-07-04 13:19:38 +09:30
first + + ;
}
}
2023-07-25 11:09:43 +09:30
static void check_unique ( struct param * copy ,
2022-07-04 13:19:38 +09:30
int ( * compar ) ( const struct param * a ,
const struct param * b , void * unused ) )
{
asort ( copy , tal_count ( copy ) , compar , NULL ) ;
2023-07-25 11:09:43 +09:30
check_distinct ( copy , compar ) ;
2022-07-04 13:19:38 +09:30
}
/*
* Verify consistent internal state .
*/
2023-07-25 11:09:43 +09:30
static void check_params ( const struct param * params )
2022-07-04 13:19:38 +09:30
{
if ( tal_count ( params ) < 2 )
2023-07-25 11:09:43 +09:30
return ;
2022-07-04 13:19:38 +09:30
/* make sure there are no required params following optional */
2023-07-25 11:09:43 +09:30
check_distinct ( params , comp_req_order ) ;
2022-07-04 13:19:38 +09:30
/* duplicate so we can sort */
struct param * copy = tal_dup_talarr ( params , struct param , params ) ;
/* check for repeated names and args */
2023-07-25 11:09:43 +09:30
check_unique ( copy , comp_by_name ) ;
2022-07-04 13:19:38 +09:30
tal_free ( copy ) ;
}
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 ( 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 ;
2024-01-25 10:58:55 +10:30
param_add ( & params , " subcommand " , PARAM_REQUIRED , NULL , NULL ,
( void * ) param_string , & subcmd ) ;
2022-07-04 13:19:38 +09:30
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 ] ) ;
2023-07-25 11:09:43 +09:30
check_params ( params ) ;
2022-07-04 13:19:38 +09:30
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 ;
}
2023-10-24 12:59:45 +10:30
static bool param_core ( struct command * cmd ,
const char * buffer ,
const jsmntok_t tokens [ ] ,
va_list ap )
2022-07-04 13:19:38 +09:30
{
struct param * params = tal_arr ( tmpctx , struct param , 0 ) ;
const char * name ;
bool allow_extra = false ;
while ( ( name = va_arg ( ap , const char * ) ) ! = NULL ) {
enum param_style style = va_arg ( ap , enum param_style ) ;
2024-01-25 10:58:55 +10:30
const char * depr_start = va_arg ( ap , const char * ) ;
const char * depr_end = va_arg ( ap , const char * ) ;
2022-07-04 13:19:38 +09:30
param_cbx cbx = va_arg ( ap , param_cbx ) ;
void * arg = va_arg ( ap , void * ) ;
if ( streq ( name , " " ) ) {
allow_extra = true ;
continue ;
}
2024-01-25 10:58:55 +10:30
param_add ( & params , name , style , depr_start , depr_end , cbx , arg ) ;
2022-07-04 13:19:38 +09:30
}
if ( command_usage_only ( cmd ) ) {
2023-07-25 11:09:43 +09:30
check_params ( params ) ;
2022-07-04 13:19:38 +09:30
command_set_usage ( cmd , param_usage ( cmd , params ) ) ;
return false ;
}
2023-10-24 12:59:45 +10:30
return param_arr ( cmd , buffer , tokens , params , allow_extra ) = = NULL ;
}
bool param ( struct command * cmd ,
const char * buffer ,
const jsmntok_t tokens [ ] , . . . )
{
bool ret ;
va_list ap ;
va_start ( ap , tokens ) ;
ret = param_core ( cmd , buffer , tokens , ap ) ;
va_end ( ap ) ;
2024-04-04 14:06:11 +10:30
/* Always "fail" if we're just checking! */
if ( ret & & command_check_only ( cmd ) ) {
/* We really do ignore result here! */
if ( command_check_done ( cmd ) )
;
2023-10-24 12:59:45 +10:30
ret = false ;
2024-04-04 14:06:11 +10:30
}
2023-10-24 12:59:45 +10:30
return ret ;
}
bool param_check ( struct command * cmd ,
const char * buffer ,
const jsmntok_t tokens [ ] , . . . )
{
bool ret ;
va_list ap ;
va_start ( ap , tokens ) ;
ret = param_core ( cmd , buffer , tokens , ap ) ;
va_end ( ap ) ;
return ret ;
2022-07-04 13:19:38 +09:30
}
2018-12-16 15:20:06 +10:30
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 11:09:28 +10:30
{
2018-12-16 15:20:06 +10:30
if ( tok - > type = = JSMN_ARRAY ) {
* arr = tok ;
return NULL ;
}
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok , " should be an array " ) ;
2018-12-08 11:09:28 +10:30
}
2018-12-16 15:20:06 +10:30
struct command_result * param_bool ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
bool * * b )
2018-12-08 11:09:28 +10:30
{
* b = tal ( cmd , bool ) ;
2018-12-16 15:17:06 +10:30
if ( json_to_bool ( buffer , tok , * b ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be 'true' or 'false' " ) ;
2018-12-08 11:09:28 +10:30
}
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 11:09:28 +10:30
{
2020-02-19 11:11:36 +01:00
* num = tal ( cmd , uint64_t ) ;
if ( json_to_millionths ( buffer , tok , * num ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a non-negative floating-point number " ) ;
2018-12-08 11:09:28 +10:30
}
2018-12-16 15:20:06 +10:30
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 11:09:28 +10:30
{
2019-06-12 10:08:54 +09:30
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 11:09:28 +10:30
if ( * str )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
}
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a string (without \\ u) " ) ;
2018-12-08 11:09:28 +10:30
}
2018-12-16 15:20:06 +10:30
struct command_result * param_string ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
const char * * str )
2018-12-08 11:09:28 +10:30
{
* str = tal_strndup ( cmd , buffer + tok - > start ,
tok - > end - tok - > start ) ;
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
}
2023-06-13 12:33:31 +02:00
struct command_result * param_invstring ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
const char * * str )
{
const char * strtmp = json_strdup ( cmd , buffer , tok ) ;
* str = to_canonical_invstr ( cmd , strtmp ) ;
return NULL ;
}
2019-07-16 11:11:51 +09:30
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 15:20:06 +10:30
struct command_result * param_label ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
2019-06-12 10:08:54 +09:30
struct json_escape * * label )
2018-12-08 11:09:28 +10:30
{
/* We accept both strings and number literals here. */
2019-06-12 10:08:54 +09:30
* label = json_escape_string_ ( cmd , buffer + tok - > start , tok - > end - tok - > start ) ;
2018-12-08 11:09:28 +10:30
if ( * label & & ( tok - > type = = JSMN_STRING | | json_tok_is_num ( buffer , tok ) ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a string or number " ) ;
2018-12-08 11:09:28 +10:30
}
2018-12-16 15:20:06 +10:30
struct command_result * param_number ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
unsigned int * * num )
2018-12-08 11:09:28 +10:30
{
* num = tal ( cmd , unsigned int ) ;
if ( json_to_number ( buffer , tok , * num ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an integer " ) ;
2018-12-08 11:09:28 +10:30
}
2018-12-16 15:20:06 +10:30
struct command_result * param_sha256 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
struct sha256 * * hash )
2018-12-08 11:09:28 +10:30
{
* hash = tal ( cmd , struct sha256 ) ;
if ( hex_decode ( buffer + tok - > start ,
tok - > end - tok - > start ,
* hash , sizeof ( * * hash ) ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a 32 byte hex value " ) ;
2018-12-08 11:09:28 +10:30
}
2024-08-07 11:19:52 +09:30
struct command_result * param_u16 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
uint16_t * * num )
{
* num = tal ( cmd , uint16_t ) ;
if ( json_to_u16 ( buffer , tok , * num ) )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an unsigned 16 bit integer " ) ;
}
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 15:20:06 +10:30
struct command_result * param_u64 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
uint64_t * * num )
2018-12-08 11:09:28 +10:30
{
* num = tal ( cmd , uint64_t ) ;
if ( json_to_u64 ( buffer , tok , * num ) )
2018-12-16 15:20:06 +10:30
return NULL ;
2018-12-08 11:09:28 +10:30
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an unsigned 64 bit integer " ) ;
2018-12-08 11:09:28 +10:30
}
2023-07-27 14:37:52 -07:00
struct command_result * param_s64 ( struct command * cmd , const char * name ,
const char * buffer , const jsmntok_t * tok ,
int64_t * * num )
{
* num = tal ( cmd , int64_t ) ;
if ( json_to_s64 ( buffer , tok , * num ) )
return NULL ;
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be an sign 64 bit integer " ) ;
}
2019-02-21 11:15:57 +10:30
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-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a millisatoshi amount " ) ;
2019-02-21 11:15:57 +10:30
}
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-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a satoshi amount " ) ;
2019-02-21 11:15:57 +10:30
}
2019-08-16 01:41:23 +08: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-27 22:23:14 -05:00
struct command_result * param_node_id ( struct command * cmd , const char * name ,
2020-02-04 10:33:22 +10:30
const char * buffer , const jsmntok_t * tok ,
2019-08-27 22:23:14 -05:00
struct node_id * * id )
{
* id = tal ( cmd , struct node_id ) ;
if ( json_to_node_id ( buffer , tok , * id ) )
return NULL ;
2020-08-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a node id " ) ;
2019-08-27 22:23:14 -05:00
}
2019-09-30 11:31:27 -05: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-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a channel id " ) ;
2019-09-30 11:31:27 -05:00
}
2019-11-07 17:56:06 +01:00
2021-05-22 14:30:22 +09:30
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 " ) ;
}
2024-10-04 08:50:53 +09:30
struct command_result * param_short_channel_id_dir ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
struct short_channel_id_dir * * scidd )
{
* scidd = tal ( cmd , struct short_channel_id_dir ) ;
if ( ! json_to_short_channel_id_dir ( buffer , tok , * scidd ) )
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a short_channel_id_dir of form NxNxN/N " ) ;
return NULL ;
}
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-26 06:50:50 +09:30
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-26 06:50:50 +09:30
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 20:36:28 +10:30
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-26 06:50:50 +09:30
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 20:36:28 +10:30
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-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , pubkeytok ,
" should be a pubkey " ) ;
2019-11-24 19:09:19 +01:00
2019-12-05 20:36:28 +10:30
if ( ! h . raw_payload )
2020-08-26 06:50:50 +09:30
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-26 06:50:50 +09:30
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-08 06:20:21 +09:30
2020-07-08 06:20:26 +09:30
/**
2023-07-11 01:58:43 +09:30
* segwit_addr_net_decode - Try to decode a Bech32 ( m ) address and detect
2020-07-08 06:20:26 +09:30
* testnet / mainnet / regtest / signet
*
* This processes the address and returns a string if it is a Bech32
2023-07-11 01:58:43 +09:30
* address specified by BIP173 or Bech32m as by BIP350 . The string is
* set whether it is testnet or signet ( both " tb " ) , mainnet ( " bc " ) ,
* regtest ( " bcrt " ) . It does not check witness version and program size
* restrictions .
2020-07-08 06:20:26 +09:30
*
* 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 15:10:06 +08:00
witness_program_len , chainparams - > onchain_hrp ,
2020-07-08 06:20:26 +09:30
addrz ) )
2021-11-29 15:10:06 +08:00
return chainparams - > onchain_hrp ;
2020-07-08 06:20:26 +09:30
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 ;
2023-07-11 01:58:43 +09:30
const char * bech32 ;
2020-07-08 06:20:26 +09:30
u8 addr_version ;
2022-09-18 09:49:50 +09:30
if ( ripemd160_from_base58 ( & addr_version , & destination . addr ,
buffer + tok - > start , tok - > end - tok - > start ) ) {
2020-07-08 06:20:26 +09:30
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. */
2022-09-18 09:49:50 +09:30
return ADDRESS_PARSE_UNRECOGNIZED ;
2020-07-08 06:20:26 +09:30
}
/* Generate null-terminated address. */
2022-09-18 09:49:50 +09:30
addrz = tal_dup_arr ( tmpctx , char , buffer + tok - > start , tok - > end - tok - > start , 1 ) ;
2020-07-08 06:20:26 +09:30
addrz [ tok - > end - tok - > start ] = ' \0 ' ;
2023-07-11 01:58:43 +09:30
bech32 = segwit_addr_net_decode ( & witness_version , witness_program ,
2020-07-08 06:20:26 +09:30
& witness_program_len , addrz , chainparams ) ;
2023-07-11 01:58:43 +09:30
if ( bech32 ) {
2021-06-09 12:40:00 +09:30
bool witness_ok ;
2023-07-11 01:58:43 +09:30
/* Only V0 has restricted lengths of witness programs */
2021-06-09 12:40:00 +09:30
if ( witness_version = = 0 ) {
witness_ok = ( witness_program_len = = 20 | |
witness_program_len = = 32 ) ;
} else
2020-07-08 06:20:26 +09:30
witness_ok = true ;
2022-09-18 09:49:50 +09:30
if ( ! witness_ok )
return ADDRESS_PARSE_UNRECOGNIZED ;
2020-07-08 06:20:26 +09:30
2023-07-11 01:58:43 +09:30
if ( ! streq ( bech32 , chainparams - > onchain_hrp ) )
2020-07-08 06:20:26 +09:30
return ADDRESS_PARSE_WRONG_NETWORK ;
2022-09-18 09:49:50 +09:30
* scriptpubkey = scriptpubkey_witness_raw ( ctx , witness_version ,
witness_program , witness_program_len ) ;
return ADDRESS_PARSE_SUCCESS ;
2020-07-08 06:20:26 +09:30
}
2022-09-18 09:49:50 +09:30
/* Insert other parsers that accept null-terminated string here. */
2020-07-08 06:20:26 +09:30
return ADDRESS_PARSE_UNRECOGNIZED ;
}
2020-07-08 06:20:28 +09:30
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-26 06:50:50 +09:30
return command_fail_badparam ( cmd , name , buffer , tok ,
" should be a txid " ) ;
2020-07-08 06:20:28 +09:30
}
2020-08-07 10:51:33 +09:30
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 12:59:55 -05: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 10:54:34 +10:30
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 ] ;
2022-10-25 14:47:56 +02:00
/* Accept either bare ints as keys (not spec
* compliant , but simpler ) , or ints in strings , which
* are JSON spec compliant . */
if ( ! ( json_str_to_u64 ( buffer , curr , & f - > numtype ) | |
json_to_u64 ( buffer , curr , & f - > numtype ) ) ) {
2021-06-17 18:06:43 +02:00
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 21:56:06 +10:30
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 16:00:17 -05:00
2021-11-04 17:06:01 -04: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 16:00:17 -05: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 16:29:42 +09:30
* rates = lease_rates_fromhex ( cmd , buffer + tok - > start ,
tok - > end - tok - > start ) ;
if ( ! * rates )
2021-07-02 16:00:17 -05: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 13:22:34 +09:30
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 " ) ;
}