htlcswitch/link: verify an htlc is not too large in HtlcSatifiesPolicy

Before forwarding an HTLC, ensure that the amount to forward
including fees does not exceed the max HTLC set for the channel
link.
This commit is contained in:
Valentine Wallace 2018-12-08 18:21:18 -08:00
parent 90cbf9fe35
commit f0e668974e
2 changed files with 94 additions and 0 deletions

@ -1967,6 +1967,25 @@ func (l *channelLink) HtlcSatifiesPolicy(payHash [32]byte,
return failure
}
// Next, ensure that the passed HTLC isn't too large. If so, we'll cancel
// the HTLC directly.
if policy.MaxHTLC != 0 && amtToForward > policy.MaxHTLC {
l.errorf("outgoing htlc(%x) is too large: max_htlc=%v, "+
"htlc_value=%v", payHash[:], policy.MaxHTLC, amtToForward)
// As part of the returned error, we'll send our latest routing policy
// so the sending node obtains the most up-to-date data.
var failure lnwire.FailureMessage
update, err := l.cfg.FetchLastChannelUpdate(l.ShortChanID())
if err != nil {
failure = &lnwire.FailTemporaryNodeFailure{}
} else {
failure = lnwire.NewTemporaryChannelFailure(update)
}
return failure
}
// Next, using the amount of the incoming HTLC, we'll calculate the
// expected fee this incoming HTLC must carry in order to satisfy the
// constraints of the outgoing link.

@ -772,6 +772,73 @@ func TestLinkForwardMinHTLCPolicyMismatch(t *testing.T) {
}
}
// TestLinkForwardMaxHTLCPolicyMismatch tests that if a node is an intermediate
// node and receives an HTLC which is _above_ its max HTLC policy then the
// HTLC will be rejected.
func TestLinkForwardMaxHTLCPolicyMismatch(t *testing.T) {
t.Parallel()
channels, cleanUp, _, err := createClusterChannels(
btcutil.SatoshiPerBitcoin*5, btcutil.SatoshiPerBitcoin*5,
)
if err != nil {
t.Fatalf("unable to create channel: %v", err)
}
defer cleanUp()
n := newThreeHopNetwork(
t, channels.aliceToBob, channels.bobToAlice, channels.bobToCarol,
channels.carolToBob, testStartingHeight,
)
if err := n.start(); err != nil {
t.Fatal(err)
}
defer n.stop()
// In order to trigger this failure mode, we'll update our policy to have
// a new max HTLC of 10 satoshis.
maxHtlc := lnwire.NewMSatFromSatoshis(10)
// First we'll generate a route over 2 hops within the network that
// attempts to pay out an amount greater than the max HTLC we're about to
// set.
amountNoFee := maxHtlc + 1
htlcAmt, htlcExpiry, hops := generateHops(
amountNoFee, testStartingHeight, n.firstBobChannelLink,
n.carolChannelLink,
)
// We'll now update Bob's policy to set the max HTLC we chose earlier.
n.secondBobChannelLink.cfg.FwrdingPolicy.MaxHTLC = maxHtlc
// Finally, we'll make the payment which'll send an HTLC with our
// specified parameters.
firstHop := n.firstBobChannelLink.ShortChanID()
_, err = makePayment(
n.aliceServer, n.carolServer, firstHop, hops, amountNoFee,
htlcAmt, htlcExpiry,
).Wait(30 * time.Second)
// We should get an error indicating a temporary channel failure, The
// failure is temporary because this payment would be allowed if Bob
// updated his policy to increase the max HTLC.
if err == nil {
t.Fatalf("payment should have failed but didn't")
}
ferr, ok := err.(*ForwardingError)
if !ok {
t.Fatalf("expected a ForwardingError, instead got: %T", err)
}
switch ferr.FailureMessage.(type) {
case *lnwire.FailTemporaryChannelFailure:
default:
t.Fatalf("incorrect error, expected temporary channel failure, "+
"instead have: %v", err)
}
}
// TestUpdateForwardingPolicy tests that the forwarding policy for a link is
// able to be updated properly. We'll first create an HTLC that meets the
// specified policy, assert that it succeeds, update the policy (to invalidate
@ -5426,6 +5493,14 @@ func TestHtlcSatisfyPolicy(t *testing.T) {
}
})
t.Run("above maxhtlc", func(t *testing.T) {
result := link.HtlcSatifiesPolicy(hash, 1500, 1200,
200, 150, 0)
if _, ok := result.(*lnwire.FailTemporaryChannelFailure); !ok {
t.Fatalf("expected FailTemporaryChannelFailure failure code")
}
})
t.Run("insufficient fee", func(t *testing.T) {
result := link.HtlcSatifiesPolicy(hash, 1005, 1000,
200, 150, 0)