Merge pull request #1025 from Roasbeef/incoming-htlc-breach-retribution

lnwallet+test: properly generate the sender HTLC script in a contract breach scenario
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2018-04-05 17:58:31 -07:00
commit 065b39bc0b
3 files changed
+215 -58

No files matched your search

+15 -5
View File
@@ -1971,13 +1971,23 @@ 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(revokedSnapshot.Htlcs))
for i, htlc := range revokedSnapshot.Htlcs {
htlcRetributions := make([]HtlcRetribution, 0, len(revokedSnapshot.Htlcs))
for _, htlc := range revokedSnapshot.Htlcs {
var (
htlcScript []byte
err error
)
// If the HTLC is dust, then we'll skip it as it doesn't have
// an output on the commitment transaction.
if htlcIsDust(
htlc.Incoming, false,
SatPerKWeight(revokedSnapshot.FeePerKw),
htlc.Amt.ToSatoshis(), chanState.RemoteChanCfg.DustLimit,
) {
continue
}
// We'll generate the original second level witness script now,
// as we'll need it if we're revoking an HTLC output on the
// remote commitment transaction, and *they* go to the second
@@ -1994,7 +2004,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// re-generate the sender HTLC script.
if htlc.Incoming {
htlcScript, err = senderHTLCScript(
keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
keyRing.RevocationKey, htlc.RHash[:],
)
if err != nil {
@@ -2015,7 +2025,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
}
}
htlcRetributions[i] = HtlcRetribution{
htlcRetributions = append(htlcRetributions, HtlcRetribution{
SignDesc: SignDescriptor{
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret,
@@ -2031,7 +2041,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
},
SecondLevelWitnessScript: secondLevelWitnessScript,
IsIncoming: htlc.Incoming,
}
})
}
// Finally, with all the necessary data constructed, we can create the
+95
View File
@@ -5012,3 +5012,98 @@ func TestMinHTLC(t *testing.T) {
t.Fatalf("expected ErrBelowMinHTLC, instead received: %v", err)
}
}
// TestNewBreachRetributionSkipsDustHtlcs ensures that in the case of a
// contract breach, all dust HTLCs are ignored and not reflected in the
// produced BreachRetribution struct. We ignore these HTLCs as they aren't
// actually manifested on the commitment transaction, as a result we can't
// actually revoked them.
func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) {
t.Parallel()
// We'll kick off the test by creating our channels which both are
// loaded with 5 BTC each.
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
var fakeOnionBlob [lnwire.OnionPacketSize]byte
copy(fakeOnionBlob[:], bytes.Repeat([]byte{0x05}, lnwire.OnionPacketSize))
// We'll modify the dust settings on both channels to be a predictable
// value for the prurpose of the test.
dustValue := btcutil.Amount(200)
aliceChannel.channelState.LocalChanCfg.DustLimit = dustValue
aliceChannel.channelState.RemoteChanCfg.DustLimit = dustValue
bobChannel.channelState.LocalChanCfg.DustLimit = dustValue
bobChannel.channelState.RemoteChanCfg.DustLimit = dustValue
// We'll now create a series of dust HTLC's, and send then from Alice
// to Bob, finally locking both of them in.
var bobPreimage [32]byte
copy(bobPreimage[:], bytes.Repeat([]byte{0xbb}, 32))
for i := 0; i < 3; i++ {
rHash := sha256.Sum256(bobPreimage[:])
h := &lnwire.UpdateAddHTLC{
PaymentHash: rHash,
Amount: lnwire.NewMSatFromSatoshis(dustValue),
Expiry: uint32(10),
OnionBlob: fakeOnionBlob,
}
htlcIndex, err := aliceChannel.AddHTLC(h, nil)
if err != nil {
t.Fatalf("unable to add bob's htlc: %v", err)
}
h.ID = htlcIndex
if _, err := bobChannel.ReceiveHTLC(h); err != nil {
t.Fatalf("unable to recv bob's htlc: %v", err)
}
}
// With the HTLC's applied to both update logs, we'll initiate a state
// transition from Alice.
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
t.Fatalf("unable to complete bob's state transition: %v", err)
}
// At this point, we'll capture the current state number, as well as
// the current commitment.
revokedCommit := bobChannel.channelState.LocalCommitment.CommitTx
revokedStateNum := aliceChannel.channelState.LocalCommitment.CommitHeight
// We'll now have Bob settle those HTLC's to Alice and then advance
// forward to a new state.
for i := 0; i < 3; i++ {
err := bobChannel.SettleHTLC(bobPreimage, uint64(i), nil, nil, nil)
if err != nil {
t.Fatalf("unable to settle htlc: %v", err)
}
err = aliceChannel.ReceiveHTLCSettle(bobPreimage, uint64(i))
if err != nil {
t.Fatalf("unable to settle htlc: %v", err)
}
}
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
t.Fatalf("unable to complete bob's state transition: %v", err)
}
// At this point, we'll now simulate a contract breach by Bob using the
// NewBreachRetribution method.
breachRet, err := NewBreachRetribution(
aliceChannel.channelState, revokedStateNum, revokedCommit, 100,
)
if err != nil {
t.Fatalf("unable to create breach retribution: %v", err)
}
// The retribution shouldn't have any HTLCs set as they were all below
// dust for both parties.
if len(breachRet.HtlcRetributions) != 0 {
t.Fatalf("zero HTLC retributions should have been created, "+
"instead %v were", len(breachRet.HtlcRetributions))
}
}