@ -12,6 +12,7 @@
# include <common/key_derive.h>
# include <common/keyset.h>
# include <common/memleak.h>
# include <common/overflows.h>
# include <common/peer_billboard.h>
# include <common/status.h>
# include <common/subdaemon.h>
@ -695,6 +696,224 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx,
return tx ;
}
/** replace_penalty_tx_to_us
*
* @ brief creates a replacement TX for
* a given penalty tx .
*
* @ param ctx - the context to allocate
* off of .
* @ param hsm_sign_msg - function to construct
* the signing message to HSM .
* @ param penalty_tx - the original
* penalty transaction .
* @ param output_amount - the output
* amount to use instead of the
* original penalty transaction .
* If this amount is below the dust
* limit , the output is replaced with
* an ` OP_RETURN ` instead .
*
* @ return the signed transaction .
*/
static struct bitcoin_tx *
replace_penalty_tx_to_us ( const tal_t * ctx ,
u8 * ( * hsm_sign_msg ) ( const tal_t * ctx ,
struct bitcoin_tx * tx ,
const u8 * wscript ) ,
const struct bitcoin_tx * penalty_tx ,
struct amount_sat output_amount )
{
struct bitcoin_tx * tx ;
/* The penalty tx input. */
const struct wally_tx_input * input ;
/* Specs of the penalty tx input. */
struct bitcoin_txid input_txid ;
u32 input_outnum ;
u8 * input_wscript ;
u8 * input_element ;
struct amount_sat input_amount ;
/* Signature from the HSM. */
u8 * msg ;
struct bitcoin_signature sig ;
/* Witness we generate from the signature and other data. */
u8 * * witness ;
/* Get the single input of the penalty tx. */
input = & penalty_tx - > wtx - > inputs [ 0 ] ;
/* Extract the input-side data. */
bitcoin_tx_input_get_txid ( penalty_tx , 0 , & input_txid ) ;
input_outnum = input - > index ;
input_wscript = tal_dup_arr ( tmpctx , u8 ,
input - > witness - > items [ 2 ] . witness ,
input - > witness - > items [ 2 ] . witness_len ,
0 ) ;
input_element = tal_dup_arr ( tmpctx , u8 ,
input - > witness - > items [ 1 ] . witness ,
input - > witness - > items [ 1 ] . witness_len ,
0 ) ;
input_amount = psbt_input_get_amount ( penalty_tx - > psbt , 0 ) ;
/* Create the replacement. */
tx = bitcoin_tx ( ctx , chainparams , 1 , 1 , /*locktime*/ 0 ) ;
/* Reconstruct the input. */
bitcoin_tx_add_input ( tx , & input_txid , input_outnum ,
BITCOIN_TX_RBF_SEQUENCE ,
NULL , input_amount , NULL , input_wscript ) ;
/* Reconstruct the output with a smaller amount. */
if ( amount_sat_greater ( output_amount , dust_limit ) )
bitcoin_tx_add_output ( tx ,
scriptpubkey_p2wpkh ( tx ,
& our_wallet_pubkey ) ,
NULL ,
output_amount ) ;
else
bitcoin_tx_add_output ( tx ,
scriptpubkey_opreturn_padded ( tx ) ,
NULL ,
AMOUNT_SAT ( 0 ) ) ;
/* Finalize the transaction. */
bitcoin_tx_finalize ( tx ) ;
/* Ask HSM to sign it. */
if ( ! wire_sync_write ( HSM_FD , take ( hsm_sign_msg ( NULL , tx ,
input_wscript ) ) ) )
status_failed ( STATUS_FAIL_HSM_IO , " While feebumping penalty: writing sign request to hsm " ) ;
msg = wire_sync_read ( tmpctx , HSM_FD ) ;
if ( ! msg | | ! fromwire_hsmd_sign_tx_reply ( msg , & sig ) )
status_failed ( STATUS_FAIL_HSM_IO , " While feebumping penalty: reading sign_tx_reply: %s " , tal_hex ( tmpctx , msg ) ) ;
/* Install the witness with the signature. */
witness = bitcoin_witness_sig_and_element ( tx , & sig ,
input_element ,
tal_bytelen ( input_element ) ,
input_wscript ) ;
bitcoin_tx_input_set_witness ( tx , 0 , take ( witness ) ) ;
return tx ;
}
/** min_rbf_bump
*
* @ brief computes the minimum RBF bump required by
* BIP125 , given an index .
*
* @ desc BIP125 requires that an replacement transaction
* pay , not just the fee of the evicted transactions ,
* but also the minimum relay fee for itself .
* This function assumes that previous RBF attempts
* paid exactly the return value for that attempt , on
* top of the initial transaction fee .
* It can serve as a baseline for other functions that
* compute a suggested fee : get whichever is higher ,
* the fee this function suggests , or your own unique
* function .
*
* This function is provided as a common function that
* all RBF - bump computations can use .
*
* @ param weight - the weight of the transaction you
* are RBFing .
* @ param index - 0 makes no sense , 1 means this is
* the first RBF attempt , 2 means this is the 2 nd
* RBF attempt , etc .
*
* @ return the suggested total fee .
*/
static struct amount_sat min_rbf_bump ( size_t weight ,
size_t index )
{
struct amount_sat min_relay_fee ;
struct amount_sat min_rbf_bump ;
/* Compute the minimum relay fee for a transaction of the given
* weight . */
min_relay_fee = amount_tx_fee ( min_relay_feerate , weight ) ;
/* For every RBF attempt, we add the min-relay-fee.
* Or in other words , we multiply the min - relay - fee by the
* index number of the attempt .
*/
if ( mul_overflows_u64 ( index , min_relay_fee . satoshis ) ) /* Raw: multiplication. */
min_rbf_bump = AMOUNT_SAT ( UINT64_MAX ) ;
else
min_rbf_bump . satoshis = index * min_relay_fee . satoshis ; /* Raw: multiplication. */
return min_rbf_bump ;
}
/** compute_penalty_output_amount
*
* @ brief computes the appropriate output amount for a
* penalty transaction that spends a theft transaction
* that is already of a specific depth .
*
* @ param initial_amount - the outout amount of the first
* penalty transaction .
* @ param depth - the current depth of the theft
* transaction .
* @ param max_depth - the maximum depth of the theft
* transaction , after which the theft transaction will
* succeed .
* @ param weight - the weight of the first penalty
* transaction , in Sipa .
*/
static struct amount_sat
compute_penalty_output_amount ( struct amount_sat initial_amount ,
u32 depth , u32 max_depth ,
size_t weight )
{
struct amount_sat max_output_amount ;
struct amount_sat output_amount ;
struct amount_sat deducted_amount ;
assert ( depth < = max_depth ) ;
assert ( depth > 0 ) ;
/* The difference between initial_amount, and the fee suggested
* by min_rbf_bump , is the largest allowed output amount .
*
* depth = 1 is the first attempt , thus maps to the 0 th RBF
* ( i . e . the initial attempt that is not RBFed itself ) .
* We actually start to replace at depth = 2 , so we use
* depth - 1 as the index for min_rbf_bump .
*/
if ( ! amount_sat_sub ( & max_output_amount ,
initial_amount , min_rbf_bump ( weight , depth - 1 ) ) )
/* If min_rbf_bump is larger than the initial_amount,
* we should just donate the whole output as fee ,
* meaning we get 0 output amount .
*/
return AMOUNT_SAT ( 0 ) ;
/* Map the depth / max_depth into a number between 0->1. */
double x = ( double ) depth / ( double ) max_depth ;
/* Get the cube of the above position, resulting in a graph
* where the y is close to 0 up to less than halfway through ,
* then quickly rises up to 1 as depth nears the max depth .
*/
double y = x * x * x ;
/* Scale according to the initial_amount. */
deducted_amount . satoshis = ( u64 ) ( y * initial_amount . satoshis ) ; /* Raw: multiplication. */
/* output_amount = initial_amount - deducted_amount. */
if ( ! amount_sat_sub ( & output_amount ,
initial_amount , deducted_amount ) )
/* If underflow, force to 0. */
output_amount = AMOUNT_SAT ( 0 ) ;
/* If output exceeds max, return max. */
if ( amount_sat_less ( max_output_amount , output_amount ) )
return max_output_amount ;
return output_amount ;
}
static void hsm_sign_local_htlc_tx ( struct bitcoin_tx * tx ,
const u8 * wscript ,
struct bitcoin_signature * sig )
@ -824,8 +1043,110 @@ static enum wallet_tx_type onchain_txtype_to_wallet_txtype(enum tx_type t)
abort ( ) ;
}
/** proposal_is_rbfable
*
* @ brief returns true if the given proposal
* would be RBFed if the output it is tracking
* increases in depth without being spent .
*/
static bool proposal_is_rbfable ( const struct proposed_resolution * proposal )
{
/* Future onchain resolutions, such as anchored commitments, might
* want to RBF as well .
*/
return proposal - > tx_type = = OUR_PENALTY_TX ;
}
/** proposal_should_rbf
*
* @ brief the given output just increased its depth ,
* so the proposal for it should be RBFed and
* rebroadcast .
*
* @ desc precondition : the given output must have an
* rbfable proposal as per ` proposal_is_rbfable ` .
*/
static void proposal_should_rbf ( struct tracked_output * out , bool is_replay )
{
struct bitcoin_tx * tx = NULL ;
u32 depth ;
assert ( out - > proposal ) ;
assert ( proposal_is_rbfable ( out - > proposal ) ) ;
depth = out - > depth ;
/* Do not RBF at depth 1.
*
* Since we react to * onchain * events , whatever proposal we made ,
* the output for that proposal is already at depth 1.
*
* Since our initial proposal was broadcasted with the output at
* depth 1 , we should not RBF until a new block arrives , which is
* at depth 2.
*/
if ( depth < = 1 )
return ;
if ( out - > proposal - > tx_type = = OUR_PENALTY_TX ) {
u32 max_depth = to_self_delay [ REMOTE ] ;
u32 my_depth = depth ;
size_t weight = bitcoin_tx_weight ( out - > proposal - > tx ) ;
struct amount_sat initial_amount ;
struct amount_sat new_amount ;
if ( max_depth > = 1 )
max_depth - = 1 ;
if ( my_depth > = max_depth )
my_depth = max_depth ;
bitcoin_tx_output_get_amount_sat ( out - > proposal - > tx , 0 ,
& initial_amount ) ;
/* Compute the new output amount for the RBF. */
new_amount = compute_penalty_output_amount ( initial_amount ,
my_depth , max_depth ,
weight ) ;
assert ( amount_sat_less_eq ( new_amount , initial_amount ) ) ;
/* Recreate the penalty tx. */
tx = replace_penalty_tx_to_us ( tmpctx ,
& penalty_to_us ,
out - > proposal - > tx , new_amount ) ;
status_debug ( " Created RBF OUR_PENALTY_TX with output %s "
" (originally %s) for depth % " PRIu32 " /% " PRIu32 " . " ,
type_to_string ( tmpctx , struct amount_sat ,
& new_amount ) ,
type_to_string ( tmpctx , struct amount_sat ,
& initial_amount ) ,
depth , to_self_delay [ LOCAL ] ) ;
}
/* Add other RBF-able proposals here. */
/* Broadcast the transaction. */
if ( tx ) {
enum wallet_tx_type wtt ;
status_debug ( " Broadcasting RBF %s (%s) to resolve %s/%s "
" depth=% " PRIu32 " " ,
tx_type_name ( out - > proposal - > tx_type ) ,
type_to_string ( tmpctx , struct bitcoin_tx , tx ) ,
tx_type_name ( out - > tx_type ) ,
output_type_name ( out - > output_type ) ,
depth ) ;
wtt = onchain_txtype_to_wallet_txtype ( out - > proposal - > tx_type ) ;
wire_sync_write ( REQ_FD ,
take ( towire_onchaind_broadcast_tx ( NULL , tx ,
wtt ,
true ) ) ) ;
}
}
static void proposal_meets_depth ( struct tracked_output * out , bool is_replay )
{
bool is_rbf = false ;
/* If we simply wanted to ignore it after some depth */
if ( ! out - > proposal - > tx ) {
ignore_output ( out ) ;
@ -838,12 +1159,16 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay)
tx_type_name ( out - > tx_type ) ,
output_type_name ( out - > output_type ) ) ;
if ( out - > proposal )
/* Our own penalty transactions are going to be RBFed. */
is_rbf = proposal_is_rbfable ( out - > proposal ) ;
wire_sync_write (
REQ_FD ,
take ( towire_onchaind_broadcast_tx (
NULL , out - > proposal - > tx ,
onchain_txtype_to_wallet_txtype ( out - > proposal - > tx_type ) ,
false ) ) ) ;
is_rbf ) ) ) ;
/* Don't wait for this if we're ignoring the tiny payment. */
if ( out - > proposal - > tx_type = = IGNORING_TINY_PAYMENT ) {
@ -1361,7 +1686,7 @@ static void steal_htlc_tx(struct tracked_output *out,
* ` < revocationsig > 1 `
*/
tx = tx_to_us ( htlc_out , penalty_to_us , htlc_out ,
0xFFFFFFFF , 0 ,
BITCOIN_TX_RBF_SEQUENCE , 0 ,
& ONE , sizeof ( ONE ) ,
htlc_out - > wscript ,
& tx_type , penalty_feerate ) ;
@ -1616,6 +1941,13 @@ static void tx_new_depth(struct tracked_output **outs,
& & depth > = outs [ i ] - > proposal - > depth_required ) {
proposal_meets_depth ( outs [ i ] , is_replay ) ;
}
/* Otherwise, is this an output whose proposed resolution
* we should RBF ? */
if ( outs [ i ] - > proposal
& & bitcoin_txid_eq ( & outs [ i ] - > txid , txid )
& & proposal_is_rbfable ( outs [ i ] - > proposal ) )
proposal_should_rbf ( outs [ i ] , is_replay ) ;
}
}
@ -2502,8 +2834,8 @@ static void steal_to_them_output(struct tracked_output *out, bool is_replay)
& keyset - > self_revocation_key ,
& keyset - > self_delayed_payment_key ) ;
tx = tx_to_us ( tmpctx , penalty_to_us , out , 0xFFFFFFFF , 0 , & ONE ,
sizeof ( ONE ) , wscript , & tx_type , penalty_feerate ) ;
tx = tx_to_us ( tmpctx , penalty_to_us , out , BITCOIN_TX_RBF_SEQUENCE , 0 ,
& ONE , sizeof ( ONE ) , wscript , & tx_type , penalty_feerate ) ;
propose_resolution ( out , tx , 0 , tx_type , is_replay ) ;
}
@ -2522,8 +2854,9 @@ static void steal_htlc(struct tracked_output *out, bool is_replay)
* < revocation_sig > < revocationpubkey >
*/
pubkey_to_der ( der , & keyset - > self_revocation_key ) ;
tx = tx_to_us ( out , penalty_to_us , out , 0xFFFFFFFF , 0 , der , sizeof ( der ) ,
out - > wscript , & tx_type , penalty_feerate ) ;
tx = tx_to_us ( out , penalty_to_us , out , BITCOIN_TX_RBF_SEQUENCE , 0 ,
der , sizeof ( der ) , out - > wscript , & tx_type ,
penalty_feerate ) ;
propose_resolution ( out , tx , 0 , tx_type , is_replay ) ;
}