lnwallet: the BreachRetribution struct is now aware of HTLC outputs
This commit adds awareness of active HTLC outputs to the BreachRetribution struct. Previously, in the case of a breach, the struct was only populated with enough information to sweep the two commitment outputs. With this commit, the struct now has enough information to sweep _all_ outputs within the commitment transaction.
This commit is contained in:
parent
5a78f80ffe
commit
a4c07d061e
@ -5,6 +5,8 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@ -1002,6 +1004,21 @@ func (lc *LightningChannel) Stop() {
|
|||||||
|
|
||||||
lc.wg.Wait()
|
lc.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HtlcRetribution contains all the items necessary to seep a revoked HTLC
|
||||||
|
// transaction from a revoked commitment transaction broadcast by the remot
|
||||||
|
// party.
|
||||||
|
type HtlcRetribution struct {
|
||||||
|
// SignDesc is a design descriptor capable of generating the necessary
|
||||||
|
// signatures to satisfy the revocation clause of the HTLC's public key
|
||||||
|
// script.
|
||||||
|
SignDesc SignDescriptor
|
||||||
|
|
||||||
|
// OutPoint is the target outpoint of this HTLC pointing to the
|
||||||
|
// breached commitment transaction.
|
||||||
|
OutPoint wire.OutPoint
|
||||||
|
}
|
||||||
|
|
||||||
// BreachRetribution contains all the data necessary to bring a channel
|
// BreachRetribution contains all the data necessary to bring a channel
|
||||||
// counterparty to justice claiming ALL lingering funds within the channel in
|
// counterparty to justice claiming ALL lingering funds within the channel in
|
||||||
// the scenario that they broadcast a revoked commitment transaction. A
|
// the scenario that they broadcast a revoked commitment transaction. A
|
||||||
@ -1025,7 +1042,7 @@ type BreachRetribution struct {
|
|||||||
// LocalOutputSignDesc is a SignDescriptor which is capable of
|
// LocalOutputSignDesc is a SignDescriptor which is capable of
|
||||||
// generating the signature necessary to sweep the output within the
|
// generating the signature necessary to sweep the output within the
|
||||||
// BreachTransaction that pays directly us.
|
// BreachTransaction that pays directly us.
|
||||||
LocalOutputSignDesc *SignDescriptor
|
LocalOutputSignDesc SignDescriptor
|
||||||
|
|
||||||
// LocalOutpoint is the outpoint of the output paying to us (the local
|
// LocalOutpoint is the outpoint of the output paying to us (the local
|
||||||
// party) within the breach transaction.
|
// party) within the breach transaction.
|
||||||
@ -1035,11 +1052,15 @@ type BreachRetribution struct {
|
|||||||
// generating the signature required to claim the funds as described
|
// generating the signature required to claim the funds as described
|
||||||
// within the revocation clause of the remote party's commitment
|
// within the revocation clause of the remote party's commitment
|
||||||
// output.
|
// output.
|
||||||
RemoteOutputSignDesc *SignDescriptor
|
RemoteOutputSignDesc SignDescriptor
|
||||||
|
|
||||||
// RemoteOutpoint is the output of the output paying to the remote
|
// RemoteOutpoint is the output of the output paying to the remote
|
||||||
// party within the breach transaction.
|
// party within the breach transaction.
|
||||||
RemoteOutpoint wire.OutPoint
|
RemoteOutpoint wire.OutPoint
|
||||||
|
|
||||||
|
// HtlcRetributions is a slice of HTLC retributions for each output
|
||||||
|
// active HTLC output within the breached commitment transaction.
|
||||||
|
HtlcRetributions []HtlcRetribution
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBreachRetribution creates a new fully populated BreachRetribution for the
|
// newBreachRetribution creates a new fully populated BreachRetribution for the
|
||||||
@ -1064,21 +1085,32 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
commitmentSecret, commitmentPoint := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
|
revocationPreimage[:])
|
||||||
|
|
||||||
|
// With the commitment point generated, we can now generate the four
|
||||||
|
// keys we'll need to reconstruct the commitment state,
|
||||||
|
localKey := TweakPubKey(chanState.LocalChanCfg.PaymentBasePoint,
|
||||||
|
commitmentPoint)
|
||||||
|
remoteKey := TweakPubKey(chanState.RemoteChanCfg.PaymentBasePoint,
|
||||||
|
commitmentPoint)
|
||||||
|
remoteDelayKey := TweakPubKey(chanState.RemoteChanCfg.DelayBasePoint,
|
||||||
|
commitmentPoint)
|
||||||
|
|
||||||
// Once we derive the revocation leaf, we can then re-create the
|
// Once we derive the revocation leaf, we can then re-create the
|
||||||
// revocation public key used within this state. This is needed in
|
// revocation public key used within this state. This is needed in
|
||||||
// order to create the proper script below.
|
// order to create the proper script below.
|
||||||
localCommitKey := chanState.OurCommitKey
|
revocationKey := DeriveRevocationPubkey(
|
||||||
revocationKey := DeriveRevocationPubkey(localCommitKey, revocationPreimage[:])
|
chanState.LocalChanCfg.RevocationBasePoint,
|
||||||
|
commitmentPoint,
|
||||||
remoteCommitkey := chanState.TheirCommitKey
|
)
|
||||||
remoteDelay := chanState.RemoteCsvDelay
|
|
||||||
|
|
||||||
// Next, reconstruct the scripts as they were present at this state
|
// Next, reconstruct the scripts as they were present at this state
|
||||||
// number so we can have the proper witness script to sign and include
|
// number so we can have the proper witness script to sign and include
|
||||||
// within the final witness.
|
// within the final witness.
|
||||||
remotePkScript, err := commitScriptToSelf(remoteDelay,
|
remoteDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
|
||||||
remoteCommitkey, revocationKey)
|
remotePkScript, err := commitScriptToSelf(remoteDelay, remoteDelayKey,
|
||||||
|
revocationKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1086,7 +1118,11 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
localPkScript, err := commitScriptUnencumbered(localCommitKey)
|
localPkScript, err := commitScriptUnencumbered(localKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localWitnessHash, err := witnessScriptHash(localPkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1108,6 +1144,61 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With the commitment outputs located, we'll now generate all the
|
||||||
|
// retribution structs for each of the HTLC transactions active on the
|
||||||
|
// remote commitment transaction.
|
||||||
|
htlcRetributions := make([]HtlcRetribution, len(chanState.Htlcs))
|
||||||
|
for i, htlc := range revokedSnapshot.Htlcs {
|
||||||
|
var (
|
||||||
|
htlcScript []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// If this is an incoming HTLC, then this means that they were
|
||||||
|
// the sender of the HTLC (relative to us). So we'll
|
||||||
|
// re-generate the sender HTLC script.
|
||||||
|
if htlc.Incoming {
|
||||||
|
htlcScript, err = senderHTLCScript(localKey, remoteKey,
|
||||||
|
revocationKey, htlc.RHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, is this was an outgoing HTLC that we sent, then
|
||||||
|
// from the PoV of the remote commitment state, they're the
|
||||||
|
// receiver of this HTLC.
|
||||||
|
} else {
|
||||||
|
htlcScript, err = receiverHTLCScript(
|
||||||
|
htlc.RefundTimeout, localKey, remoteKey,
|
||||||
|
revocationKey, htlc.RHash[:],
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
htlcRetributions[i] = HtlcRetribution{
|
||||||
|
SignDesc: SignDescriptor{
|
||||||
|
PubKey: chanState.LocalChanCfg.RevocationBasePoint,
|
||||||
|
DoubleTweak: commitmentSecret,
|
||||||
|
WitnessScript: htlcScript,
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: int64(htlc.Amt),
|
||||||
|
},
|
||||||
|
HashType: txscript.SigHashAll,
|
||||||
|
},
|
||||||
|
OutPoint: wire.OutPoint{
|
||||||
|
Hash: commitHash,
|
||||||
|
Index: uint32(htlc.OutputIndex),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll need to reconstruct the single tweak so we can sweep our
|
||||||
|
// non-delayed pay-to-self output self.
|
||||||
|
singleTweak := SingleTweakBytes(commitmentPoint,
|
||||||
|
chanState.LocalChanCfg.PaymentBasePoint)
|
||||||
|
|
||||||
// Finally, with all the necessary data constructed, we can create the
|
// Finally, with all the necessary data constructed, we can create the
|
||||||
// BreachRetribution struct which houses all the data necessary to
|
// BreachRetribution struct which houses all the data necessary to
|
||||||
// swiftly bring justice to the cheating remote party.
|
// swiftly bring justice to the cheating remote party.
|
||||||
@ -1116,18 +1207,20 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
RevokedStateNum: stateNum,
|
RevokedStateNum: stateNum,
|
||||||
PendingHTLCs: revokedSnapshot.Htlcs,
|
PendingHTLCs: revokedSnapshot.Htlcs,
|
||||||
LocalOutpoint: localOutpoint,
|
LocalOutpoint: localOutpoint,
|
||||||
LocalOutputSignDesc: &SignDescriptor{
|
LocalOutputSignDesc: SignDescriptor{
|
||||||
PubKey: localCommitKey,
|
SingleTweak: singleTweak,
|
||||||
|
PubKey: chanState.LocalChanCfg.PaymentBasePoint,
|
||||||
|
WitnessScript: localPkScript,
|
||||||
Output: &wire.TxOut{
|
Output: &wire.TxOut{
|
||||||
PkScript: localPkScript,
|
PkScript: localWitnessHash,
|
||||||
Value: int64(revokedSnapshot.LocalBalance),
|
Value: int64(revokedSnapshot.LocalBalance),
|
||||||
},
|
},
|
||||||
HashType: txscript.SigHashAll,
|
HashType: txscript.SigHashAll,
|
||||||
},
|
},
|
||||||
RemoteOutpoint: remoteOutpoint,
|
RemoteOutpoint: remoteOutpoint,
|
||||||
RemoteOutputSignDesc: &SignDescriptor{
|
RemoteOutputSignDesc: SignDescriptor{
|
||||||
PubKey: localCommitKey,
|
PubKey: chanState.LocalChanCfg.RevocationBasePoint,
|
||||||
PrivateTweak: revocationPreimage[:],
|
DoubleTweak: commitmentSecret,
|
||||||
WitnessScript: remotePkScript,
|
WitnessScript: remotePkScript,
|
||||||
Output: &wire.TxOut{
|
Output: &wire.TxOut{
|
||||||
PkScript: remoteWitnessHash,
|
PkScript: remoteWitnessHash,
|
||||||
@ -1135,6 +1228,7 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||||||
},
|
},
|
||||||
HashType: txscript.SigHashAll,
|
HashType: txscript.SigHashAll,
|
||||||
},
|
},
|
||||||
|
HtlcRetributions: htlcRetributions,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user