@ -1,4 +1,7 @@
# include <ccan/ccan/opt/opt.h>
# include <ccan/tal/str/str.h>
# include <common/json_command.h>
# include <common/json_command.h>
# include <common/json_helpers.h>
# include <common/jsonrpc_errors.h>
# include <common/jsonrpc_errors.h>
# include <common/wallet_tx.h>
# include <common/wallet_tx.h>
# include <inttypes.h>
# include <inttypes.h>
@ -23,13 +26,14 @@ struct command_result *param_wtx(struct command *cmd,
return NULL ;
return NULL ;
}
}
wtx - > all_funds = false ;
wtx - > all_funds = false ;
if ( ! parse_amount_sat ( & wtx - > amount ,
if ( ! parse_amount_sat ( & wtx - > amount ,
buffer + tok - > start , tok - > end - tok - > start ) )
buffer + tok - > start , tok - > end - tok - > start ) )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" '%s' should be satoshis or ' all' , not '%.*s' " ,
" '%s' should be an amount in satoshis or all, not '%.*s' " ,
name ,
name ,
tok - > end - tok - > start ,
tok - > end - tok - > start ,
buffer + tok - > start ) ;
buffer + tok - > start ) ;
if ( amount_sat_greater ( wtx - > amount , max ) )
if ( amount_sat_greater ( wtx - > amount , max ) )
return command_fail ( wtx - > cmd , FUND_MAX_EXCEEDED ,
return command_fail ( wtx - > cmd , FUND_MAX_EXCEEDED ,
@ -39,6 +43,66 @@ struct command_result *param_wtx(struct command *cmd,
return NULL ;
return NULL ;
}
}
struct command_result * param_utxos ( struct command * cmd ,
const char * name ,
const char * buffer ,
const jsmntok_t * tok ,
const struct utxo * * * utxos )
{
size_t i ;
const jsmntok_t * curr ;
struct bitcoin_txid * * txids = tal_arr ( cmd , struct bitcoin_txid * , 0 ) ;
unsigned int * * outnums = tal_arr ( cmd , unsigned int * , 0 ) ;
json_for_each_arr ( i , curr , tok ) {
jsmntok_t txid_tok , outnum_tok ;
if ( ! split_tok ( buffer , curr , ' : ' , & txid_tok , & outnum_tok ) )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode the outpoint from \" %s \" "
" The utxos should be specified as "
" 'txid:output_index'. " ,
json_strdup ( tmpctx , buffer , curr ) ) ;
struct bitcoin_txid * txid = tal ( txids , struct bitcoin_txid ) ;
unsigned int * outnum = tal ( txids , unsigned int ) ;
if ( ! json_to_txid ( buffer , ( const jsmntok_t * ) & txid_tok , txid ) ) {
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not get a txid out of \" %s \" " ,
json_strdup ( tmpctx , buffer , & txid_tok ) ) ;
}
if ( ! json_to_number ( buffer , ( const jsmntok_t * ) & outnum_tok , outnum ) )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not get a vout out of \" %s \" " ,
json_strdup ( tmpctx , buffer , & outnum_tok ) ) ;
tal_arr_expand ( & txids , txid ) ;
tal_arr_expand ( & outnums , outnum ) ;
}
if ( ! tal_count ( txids ) | | ! tal_count ( outnums ) )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Please specify an array of 'txid:output_index', "
" not \" %.*s \" " ,
tok - > end - tok - > start ,
buffer + tok - > start ) ;
* utxos = wallet_select_specific ( cmd , cmd - > ld - > wallet , txids , outnums ) ;
tal_free ( txids ) ;
tal_free ( outnums ) ;
if ( ! * utxos )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" Could not decode all of the outpoints. The utxos "
" should be specified as an array of "
" 'txid:output_index'. " ) ;
if ( tal_count ( * utxos ) = = 0 )
return command_fail ( cmd , JSONRPC2_INVALID_PARAMS ,
" No matching utxo was found from the wallet. "
" You can get a list of the wallet utxos with "
" the `listfunds` RPC call. " ) ;
return NULL ;
}
static struct command_result * check_amount ( const struct wallet_tx * wtx ,
static struct command_result * check_amount ( const struct wallet_tx * wtx ,
struct amount_sat amount )
struct amount_sat amount )
{
{
@ -104,3 +168,64 @@ struct command_result *wtx_select_utxos(struct wallet_tx *tx,
}
}
return NULL ;
return NULL ;
}
}
struct command_result * wtx_from_utxos ( struct wallet_tx * tx ,
u32 fee_rate_per_kw ,
size_t out_len ,
u32 maxheight ,
const struct utxo * * utxos )
{
size_t weight ;
struct amount_sat total_amount , fee_estimate ;
tx - > change = AMOUNT_SAT ( 0 ) ;
tx - > change_key_index = 0 ;
total_amount = AMOUNT_SAT ( 0 ) ;
/* The transaction has `tal_count(tx.utxos)` inputs and one output */
/* (version + in count + out count + locktime) (index + value + script length) */
weight = 4 * ( 4 + 1 + 1 + 4 ) + 4 * ( 8 + 1 + out_len ) ;
for ( size_t i = 0 ; i < tal_count ( utxos ) ; i + + ) {
if ( ! * utxos [ i ] - > blockheight | | * utxos [ i ] - > blockheight > maxheight ) {
tal_arr_remove ( & utxos , i ) ;
continue ;
}
/* txid + index + sequence + script_len */
weight + = ( 32 + 4 + 4 + 1 ) * 4 ;
/* P2SH variants include push of <0 <20-byte-key-hash>> */
if ( utxos [ i ] - > is_p2sh )
weight + = 23 * 4 ;
/* Account for witness (1 byte count + sig + key) */
weight + = 1 + ( 1 + 73 + 1 + 33 ) ;
if ( ! amount_sat_add ( & total_amount , total_amount , utxos [ i ] - > amount ) )
fatal ( " Overflow when computing input amount " ) ;
}
tx - > utxos = utxos ;
if ( ! tx - > all_funds & & amount_sat_less ( tx - > amount , total_amount )
& & ! amount_sat_sub ( & tx - > change , total_amount , tx - > amount ) )
fatal ( " Overflow when computing change " ) ;
if ( amount_sat_greater_eq ( tx - > change , get_chainparams ( tx - > cmd - > ld ) - > dust_limit ) ) {
tx - > change_key_index = wallet_get_newindex ( tx - > cmd - > ld ) ;
/* Add the change output's weight */
weight + = ( 8 + 1 + out_len ) * 4 ;
}
fee_estimate = amount_tx_fee ( fee_rate_per_kw , weight ) ;
if ( tx - > all_funds | | amount_sat_eq ( tx - > change , AMOUNT_SAT ( 0 ) ) ) {
tx - > amount = total_amount ;
if ( ! amount_sat_sub ( & tx - > amount , tx - > amount , fee_estimate ) )
return command_fail ( tx - > cmd , FUND_CANNOT_AFFORD ,
" Cannot afford transaction with %s sats of fees " ,
type_to_string ( tmpctx , struct amount_sat ,
& fee_estimate ) ) ;
} else {
if ( ! amount_sat_sub ( & tx - > change , tx - > change , fee_estimate ) )
return command_fail ( tx - > cmd , FUND_CANNOT_AFFORD ,
" Cannot afford transaction with %s sats of fees " ,
type_to_string ( tmpctx , struct amount_sat ,
& fee_estimate ) ) ;
}
return check_amount ( tx , tx - > amount ) ;
}