@ -27,8 +27,8 @@ _logger = get_logger(__name__)
def create_sweeptxs_for_thei r_revoked_ctx ( chan : ' Channel ' , ctx : Transaction , per_commitment_secret : bytes ,
sweep_address : str ) - > Dict [ str , Transaction ] :
def create_sweeptxs_for_wa tc htow er ( chan : ' Channel ' , ctx : Transaction , per_commitment_secret : bytes ,
sweep_address : str ) - > Dict [ str , Transaction ] :
""" Presign sweeping transactions using the just received revoked pcs.
These will only be utilised if the remote breaches .
Sweep ' to_local ' , and all the HTLCs ( two cases : directly from ctx , or from HTLC tx ) .
@ -75,8 +75,9 @@ def create_sweeptxs_for_their_revoked_ctx(chan: 'Channel', ctx: Transaction, per
sweep_address = sweep_address ,
privkey = other_revocation_privkey ,
is_revocation = True )
ctn = extract_ctn_from_tx_and_chan ( ctx , chan )
assert ctn == chan . config [ REMOTE ] . ctn
assert ctn == chan . config [ REMOTE ] . ctn - 1
# received HTLCs, in their ctx
received_htlcs = chan . included_htlcs ( REMOTE , RECEIVED , ctn )
for htlc in received_htlcs :
@ -92,6 +93,68 @@ def create_sweeptxs_for_their_revoked_ctx(chan: 'Channel', ctx: Transaction, per
return txs
def create_sweeptx_for_their_revoked_ctx ( chan : ' Channel ' , ctx : Transaction , per_commitment_secret : bytes ,
sweep_address : str ) - > Dict [ str , Transaction ] :
# prep
pcp = ecc . ECPrivkey ( per_commitment_secret ) . get_public_key_bytes ( compressed = True )
this_conf , other_conf = get_ordered_channel_configs ( chan = chan , for_us = False )
other_revocation_privkey = derive_blinded_privkey ( other_conf . revocation_basepoint . privkey ,
per_commitment_secret )
to_self_delay = other_conf . to_self_delay
this_delayed_pubkey = derive_pubkey ( this_conf . delayed_basepoint . pubkey , pcp )
txs = [ ]
# to_local
revocation_pubkey = ecc . ECPrivkey ( other_revocation_privkey ) . get_public_key_bytes ( compressed = True )
witness_script = bh2u ( make_commitment_output_to_local_witness_script (
revocation_pubkey , to_self_delay , this_delayed_pubkey ) )
to_local_address = redeem_script_to_address ( ' p2wsh ' , witness_script )
output_idx = ctx . get_output_idx_from_address ( to_local_address )
if output_idx is not None :
sweep_tx = lambda : create_sweeptx_ctx_to_local (
sweep_address = sweep_address ,
ctx = ctx ,
output_idx = output_idx ,
witness_script = witness_script ,
privkey = other_revocation_privkey ,
is_revocation = True )
return sweep_tx
def create_sweeptx_for_their_revoked_htlc ( chan : ' Channel ' , ctx : Transaction , htlc_tx : Transaction ,
sweep_address : str ) - > Dict [ str , Transaction ] :
x = analyze_ctx ( chan , ctx )
if not x :
return
ctn , their_pcp , is_revocation , per_commitment_secret = x
if not is_revocation :
return
# prep
pcp = ecc . ECPrivkey ( per_commitment_secret ) . get_public_key_bytes ( compressed = True )
this_conf , other_conf = get_ordered_channel_configs ( chan = chan , for_us = False )
other_revocation_privkey = derive_blinded_privkey ( other_conf . revocation_basepoint . privkey ,
per_commitment_secret )
to_self_delay = other_conf . to_self_delay
this_delayed_pubkey = derive_pubkey ( this_conf . delayed_basepoint . pubkey , pcp )
# same witness script as to_local
revocation_pubkey = ecc . ECPrivkey ( other_revocation_privkey ) . get_public_key_bytes ( compressed = True )
witness_script = bh2u ( make_commitment_output_to_local_witness_script (
revocation_pubkey , to_self_delay , this_delayed_pubkey ) )
htlc_address = redeem_script_to_address ( ' p2wsh ' , witness_script )
# check that htlc_tx is a htlc
if htlc_tx . outputs ( ) [ 0 ] . address != htlc_address :
return
gen_tx = lambda : create_sweeptx_ctx_to_local (
sweep_address = sweep_address ,
ctx = htlc_tx ,
output_idx = 0 ,
witness_script = witness_script ,
privkey = other_revocation_privkey ,
is_revocation = True )
return ' redeem_htlc2 ' , 0 , 0 , gen_tx
def create_sweeptxs_for_our_ctx ( chan : ' Channel ' , ctx : Transaction , ctn : int ,
sweep_address : str ) - > Dict [ str , Transaction ] :
""" Handle the case where we force close unilaterally with our latest ctx.
@ -99,6 +162,7 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
' to_local ' can be swept even if this is a breach ( by us ) ,
but HTLCs cannot ( old HTLCs are no longer stored ) .
"""
ctn = extract_ctn_from_tx_and_chan ( ctx , chan )
our_conf , their_conf = get_ordered_channel_configs ( chan = chan , for_us = True )
our_per_commitment_secret = get_per_commitment_secret_from_seed (
our_conf . per_commitment_secret_seed , RevocationStore . START_INDEX - ctn )
@ -116,12 +180,18 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
to_local_address = redeem_script_to_address ( ' p2wsh ' , to_local_witness_script )
their_payment_pubkey = derive_pubkey ( their_conf . payment_basepoint . pubkey , our_pcp )
to_remote_address = make_commitment_output_to_remote_address ( their_payment_pubkey )
# test ctx
_logger . debug ( f ' testing our ctx: { to_local_address } { to_remote_address } ' )
if ctx . get_output_idx_from_address ( to_local_address ) is None \
and ctx . get_output_idx_from_address ( to_remote_address ) is None :
return
# we have to_local, to_remote.
# other outputs are htlcs
# if they are spent, we need to generate the script
# so, second-stage htlc sweep should not be returned here
if ctn != our_conf . ctn :
_logger . info ( " we breached. " )
return { }
txs = { }
# to_local
output_idx = ctx . get_output_idx_from_address ( to_local_address )
@ -155,7 +225,7 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
preimage = preimage ,
is_received_htlc = is_received_htlc )
sweep_tx = lambda : create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx (
' sweep_from_ our_ctx_htlc_' ,
' our_ctx_htlc_ ' ,
to_self_delay = to_self_delay ,
htlc_tx = htlc_tx ,
htlctx_witness_script = htlctx_witness_script ,
@ -165,6 +235,7 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
# side effect
txs [ htlc_tx . prevout ( 0 ) ] = ( ' first-stage-htlc ' , 0 , htlc_tx . cltv_expiry , lambda : htlc_tx )
txs [ htlc_tx . txid ( ) + ' :0 ' ] = ( ' second-stage-htlc ' , to_self_delay , 0 , sweep_tx )
# offered HTLCs, in our ctx --> "timeout"
# received HTLCs, in our ctx --> "success"
offered_htlcs = chan . included_htlcs ( LOCAL , SENT , ctn ) # type: List[UpdateAddHtlc]
@ -175,17 +246,11 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
create_txns_for_htlc ( htlc , is_received_htlc = True )
return txs
def create_sweeptxs_for_their_ctx ( chan : ' Channel ' , ctx : Transaction , ctn : int ,
sweep_address : str ) - > Dict [ str , Transaction ] :
""" Handle the case when the remote force-closes with their ctx.
Sweep outputs that do not have a CSV delay ( ' to_remote ' and first - stage HTLCs ) .
Outputs with CSV delay ( ' to_local ' and second - stage HTLCs ) are redeemed by LNWatcher .
"""
our_conf , their_conf = get_ordered_channel_configs ( chan = chan , for_us = True )
ctn = extract_ctn_from_tx_and_chan ( ctx , chan )
def analyze_ctx ( chan , ctx ) :
# note: the remote sometimes has two valid non-revoked commitment transactions,
# either of which could be broadcast (their_conf.ctn, their_conf.ctn+1)
our_conf , their_conf = get_ordered_channel_configs ( chan = chan , for_us = True )
ctn = extract_ctn_from_tx_and_chan ( ctx , chan )
per_commitment_secret = None
if ctn == their_conf . ctn :
their_pcp = their_conf . current_per_commitment_point
@ -200,9 +265,23 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
return
their_pcp = ecc . ECPrivkey ( per_commitment_secret ) . get_public_key_bytes ( compressed = True )
is_revocation = True
our_revocation_privkey = derive_blinded_privkey ( our_conf . revocation_basepoint . privkey , per_commitment_secret )
#_logger.info(f'tx for revoked: {list(txs.keys())}' )
else :
return
return ctn , their_pcp , is_revocation , per_commitment_secret
def create_sweeptxs_for_their_ctx ( chan : ' Channel ' , ctx : Transaction , ctn : int ,
sweep_address : str ) - > Dict [ str , Transaction ] :
""" Handle the case when the remote force-closes with their ctx.
Sweep outputs that do not have a CSV delay ( ' to_remote ' and first - stage HTLCs ) .
Outputs with CSV delay ( ' to_local ' and second - stage HTLCs ) are redeemed by LNWatcher .
"""
txs = { }
our_conf , their_conf = get_ordered_channel_configs ( chan = chan , for_us = True )
x = analyze_ctx ( chan , ctx )
if not x :
return
ctn , their_pcp , is_revocation , per_commitment_secret = x
# to_local and to_remote addresses
our_revocation_pubkey = derive_blinded_pubkey ( our_conf . revocation_basepoint . pubkey , their_pcp )
their_delayed_pubkey = derive_pubkey ( their_conf . delayed_basepoint . pubkey , their_pcp )
@ -211,10 +290,18 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
to_local_address = redeem_script_to_address ( ' p2wsh ' , witness_script )
our_payment_pubkey = derive_pubkey ( our_conf . payment_basepoint . pubkey , their_pcp )
to_remote_address = make_commitment_output_to_remote_address ( our_payment_pubkey )
# test ctx
# test if this is their ctx
_logger . debug ( f ' testing their ctx: { to_local_address } { to_remote_address } ' )
if ctx . get_output_idx_from_address ( to_local_address ) is None \
and ctx . get_output_idx_from_address ( to_remote_address ) is None :
return
if is_revocation :
our_revocation_privkey = derive_blinded_privkey ( our_conf . revocation_basepoint . privkey , per_commitment_secret )
gen_tx = create_sweeptx_for_their_revoked_ctx ( chan , ctx , per_commitment_secret , chan . sweep_address )
if gen_tx :
tx = gen_tx ( )
txs [ tx . prevout ( 0 ) ] = ( ' to_local_for_revoked_ctx ' , 0 , 0 , gen_tx )
# prep
our_htlc_privkey = derive_privkey ( secret = int . from_bytes ( our_conf . htlc_basepoint . privkey , ' big ' ) , per_commitment_point = their_pcp )
our_htlc_privkey = ecc . ECPrivkey . from_secret_scalar ( our_htlc_privkey )
@ -223,7 +310,6 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
our_payment_privkey = derive_privkey ( our_payment_bp_privkey . secret_scalar , their_pcp )
our_payment_privkey = ecc . ECPrivkey . from_secret_scalar ( our_payment_privkey )
assert our_payment_pubkey == our_payment_privkey . get_public_key_bytes ( compressed = True )
txs = { }
# to_local is handled by lnwatcher
# to_remote
output_idx = ctx . get_output_idx_from_address ( to_remote_address )
@ -268,7 +354,7 @@ def create_sweeptxs_for_their_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
privkey = our_revocation_privkey if is_revocation else our_htlc_privkey . get_secret_bytes ( ) ,
is_revocation = is_revocation ,
cltv_expiry = cltv_expiry )
name = f ' their_ctx_sweep_ htlc_ { ctx . txid ( ) [ : 8 ] } _ { output_idx } '
name = f ' their_ctx_htlc_ { output_idx } '
txs [ prevout ] = ( name , 0 , cltv_expiry , sweep_tx )
# received HTLCs, in their ctx --> "timeout"
received_htlcs = chan . included_htlcs ( REMOTE , RECEIVED , ctn = ctn ) # type: List[UpdateAddHtlc]
@ -327,7 +413,7 @@ def create_sweeptx_their_ctx_htlc(ctx: Transaction, witness_script: bytes, sweep
if outvalue < = dust_threshold ( ) : return None
sweep_outputs = [ TxOutput ( TYPE_ADDRESS , sweep_address , outvalue ) ]
tx = Transaction . from_io ( sweep_inputs , sweep_outputs , version = 2
, name = f ' their_ctx_sweep_ htlc_ { ctx . txid ( ) [ : 8 ] } _ { output_idx } '
, name = f ' their_ctx_htlc_ { output_idx } '
# note that cltv_expiry, and therefore also locktime will be zero when breach!
, cltv_expiry = cltv_expiry , locktime = cltv_expiry )
sig = bfh ( tx . sign_txin ( 0 , privkey ) )
@ -431,7 +517,8 @@ def create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(
outvalue = val - fee
if outvalue < = dust_threshold ( ) : return None
sweep_outputs = [ TxOutput ( TYPE_ADDRESS , sweep_address , outvalue ) ]
tx = Transaction . from_io ( sweep_inputs , sweep_outputs , version = 2 , name = name_prefix + htlc_tx . txid ( ) , csv_delay = to_self_delay )
name = name_prefix + htlc_tx . txid ( ) [ 0 : 4 ]
tx = Transaction . from_io ( sweep_inputs , sweep_outputs , version = 2 , name = name , csv_delay = to_self_delay )
sig = bfh ( tx . sign_txin ( 0 , privkey ) )
witness = construct_witness ( [ sig , int ( is_revocation ) , htlctx_witness_script ] )