lnwallet test: add channel constraints tests

This commit adds the tests TestMaxAcceptedHTLCs,
TestMaxPendingAmount, TestChanReserve and TestMinHTLC.
This commit is contained in:
Johan T. Halseth 2018-02-07 20:55:34 -05:00
parent f9701cde63
commit 82dc8e0794
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
1 changed files with 356 additions and 0 deletions

View File

@ -4378,3 +4378,359 @@ func TestDesyncHTLCs(t *testing.T) {
}
// TODO(roasbeef): testing.Quick test case for retrans!!!
// TestMaxAcceptedHTLCs tests that the correct error message (ErrMaxHTLCNumber)
// is thrown when a node tries to accept more than MaxAcceptedHTLCs in a channel.
func TestMaxAcceptedHTLCs(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()
// One over the maximum number of HTLCs that either can accept.
const numHTLCs = 20
const numHTLCsReceived = 12
// Set the remote's required MaxAcceptedHtlcs. This means that alice
// can only offer the remote up to numHTLCs HTLCs.
aliceChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCs
bobChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCs
// Similarly, set the remote config's MaxAcceptedHtlcs. This means
// that the remote will be aware that Alice will only accept up to
// numHTLCsRecevied at a time.
aliceChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
bobChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
// Each HTLC amount is 0.1 BTC.
htlcAmt := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin)
// Send the maximum allowed number of HTLCs.
for i := 0; i < numHTLCs; i++ {
htlc, _ := createHTLC(i, htlcAmt)
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
}
// The next HTLC should fail with ErrMaxHTLCNumber.
htlc, _ := createHTLC(numHTLCs, htlcAmt)
_, err = aliceChannel.AddHTLC(htlc)
if err != ErrMaxHTLCNumber {
t.Fatalf("expected ErrMaxHTLCNumber, instead received: %v", err)
}
// After receiving the next HTLC, next state transition should fail
// with ErrMaxHTLCNumber.
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
err = forceStateTransition(aliceChannel, bobChannel)
if err != ErrMaxHTLCNumber {
t.Fatalf("expected ErrMaxHTLCNumber, instead received: %v", err)
}
}
// TestMaxPendingAmount tests that the maximum overall pending HTLC value is met
// given several HTLCs that, combined, exceed this value. An ErrMaxPendingAmount
// error should be returned.
func TestMaxPendingAmount(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()
// We set the remote required MaxPendingAmount to 3 BTC. We will
// attempt to overflow this value and see if it gives us the
// ErrMaxPendingAmount error.
maxPending := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin * 3)
// We set the max pending amount of Alice's config. This mean that she
// cannot offer Bob HTLCs with a total value above this limit at a given
// time.
aliceChannel.localChanCfg.MaxPendingAmount = maxPending
bobChannel.remoteChanCfg.MaxPendingAmount = maxPending
// First, we'll add 2 HTLCs of 1.5 BTC each to Alice's commitment.
// This won't trigger Alice's ErrMaxPendingAmount error.
const numHTLCs = 2
htlcAmt := lnwire.NewMSatFromSatoshis(1.5 * btcutil.SatoshiPerBitcoin)
for i := 0; i < numHTLCs; i++ {
htlc, _ := createHTLC(i, htlcAmt)
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
}
// We finally add one more HTLC of 0.1 BTC to Alice's commitment. This
// SHOULD trigger Alice's ErrMaxPendingAmount error.
htlcAmt = lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin)
htlc, _ := createHTLC(numHTLCs, htlcAmt)
_, err = aliceChannel.AddHTLC(htlc)
if err != ErrMaxPendingAmount {
t.Fatalf("expected ErrMaxPendingAmount, instead received: %v", err)
}
// And also Bob shouldn't be accepting this HTLC in the next state
// transition.
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
err = forceStateTransition(aliceChannel, bobChannel)
if err != ErrMaxPendingAmount {
t.Fatalf("expected ErrMaxPendingAmount, instead received: %v", err)
}
}
// TestChanReserve tests that the ErrBelowChanReserve error is thrown when
// an HTLC is added that causes a node's balance to dip below its channel
// reserve limit.
func TestChanReserve(t *testing.T) {
t.Parallel()
setupChannels := func() (*LightningChannel, *LightningChannel, func()) {
// 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)
}
// We set the remote required ChanReserve to 0.5 BTC. We will
// attempt to cause Alice's balance to dip below this amount and test
// whether it triggers the ErrBelowChanReserve error.
aliceMinReserve := btcutil.Amount(0.5 * btcutil.SatoshiPerBitcoin)
// Alice will need to keep her reserve above aliceMinReserve, so
// set this limit to here local config.
aliceChannel.localChanCfg.ChanReserve = aliceMinReserve
// During channel opening Bob will also get to know Alice's minimum
// reserve, and this will be found in his remote config.
bobChannel.remoteChanCfg.ChanReserve = aliceMinReserve
// We set Bob's channel reserve to a value that is larger than his
// current balance in the channel. This will ensure that after a
// channel is first opened, Bob can still receive HTLCs
// even though his balance is less than his channel reserve.
bobMinReserve := btcutil.Amount(6 * btcutil.SatoshiPerBitcoin)
bobChannel.localChanCfg.ChanReserve = bobMinReserve
aliceChannel.remoteChanCfg.ChanReserve = bobMinReserve
return aliceChannel, bobChannel, cleanUp
}
aliceChannel, bobChannel, cleanUp := setupChannels()
defer cleanUp()
aliceIndex := 0
bobIndex := 0
// Add an HTLC that will increase Bob's balance. This should
// succeed, since Alice stays above her channel reserve, and
// Bob increases his balance (while still being below his
// channel reserve).
// Resulting balances:
// Alice: 4.5
// Bob: 5.5
htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin)
htlc, _ := createHTLC(aliceIndex, htlcAmt)
aliceIndex++
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// Force a state transation, making sure this HTLC is considered
// valid even though the channel reserves are not met.
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
t.Fatalf("unable to complete state update: %v", err)
}
// Now let Bob try to add an HTLC. This should fail, since it
// will decrease his balance, which is already below the channel
// reserve.
// Resulting balances:
// Alice: 4.5
// Bob: 5.5
htlc, _ = createHTLC(bobIndex, htlcAmt)
bobIndex++
_, err := bobChannel.AddHTLC(htlc)
if err != ErrBelowChanReserve {
t.Fatalf("expected ErrBelowChanReserve, instead received: %v", err)
}
// Alice will reject this htlc when a state transition is attempted.
if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
err = forceStateTransition(aliceChannel, bobChannel)
if err != ErrBelowChanReserve {
t.Fatalf("expected ErrBelowChanReserve, instead received: %v", err)
}
// We must setup the channels again, since a violation of the channel
// constraints leads to channel shutdown.
aliceChannel, bobChannel, cleanUp = setupChannels()
defer cleanUp()
aliceIndex = 0
bobIndex = 0
// Now we'll add HTLC of 3.5 BTC to Alice's commitment, this should
// put Alice's balance at 1.5 BTC.
// Resulting balances:
// Alice: 1.5
// Bob: 9.5
htlcAmt = lnwire.NewMSatFromSatoshis(3.5 * btcutil.SatoshiPerBitcoin)
// The first HTLC should successfully be sent.
htlc, _ = createHTLC(aliceIndex, htlcAmt)
aliceIndex++
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// Add a second HTLC of 1 BTC. This should fail because it will take
// Alice's balance all the way down to her channel reserve, but
// since she is the initiator the additional transaction fee makes
// her balance dip below.
htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin)
htlc, _ = createHTLC(aliceIndex, htlcAmt)
aliceIndex++
_, err = aliceChannel.AddHTLC(htlc)
if err != ErrBelowChanReserve {
t.Fatalf("expected ErrBelowChanReserve, instead received: %v", err)
}
// Likewise, Bob will reject a state transition after this htlc is
// received, of the same reason.
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
err = forceStateTransition(aliceChannel, bobChannel)
if err != ErrBelowChanReserve {
t.Fatalf("expected ErrBelowChanReserve, instead received: %v", err)
}
// We must setup the channels again, since a violation of the channel
// constraints leads to channel shutdown.
aliceChannel, bobChannel, cleanUp = setupChannels()
defer cleanUp()
aliceIndex = 0
bobIndex = 0
// Add a HTLC of 2 BTC to Alice, and the settle it.
// Resulting balances:
// Alice: 3.0
// Bob: 7.0
htlcAmt = lnwire.NewMSatFromSatoshis(2 * btcutil.SatoshiPerBitcoin)
htlc, preimage := createHTLC(aliceIndex, htlcAmt)
aliceIndex++
aliceHtlcIndex, err := aliceChannel.AddHTLC(htlc)
if err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
bobHtlcIndex, err := bobChannel.ReceiveHTLC(htlc)
if err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
if err := bobChannel.SettleHTLC(preimage, bobHtlcIndex); err != nil {
t.Fatalf("bob unable to settle inbound htlc: %v", err)
}
if err := aliceChannel.ReceiveHTLCSettle(preimage, aliceHtlcIndex); err != nil {
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
}
// And now let Bob add an HTLC of 1 BTC. This will take Bob's balance
// all the way down to his channel reserve, but since he is not paying the
// fee this is okay.
htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin)
htlc, _ = createHTLC(bobIndex, htlcAmt)
bobIndex++
if _, err := bobChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// Do a last state transition, which should succeed.
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
t.Fatalf("unable to complete state update: %v", err)
}
}
// TestMinHTLC tests that the ErrBelowMinHTLC error is thrown if an HTLC is added
// that is below the minimm allowed value for HTLCs.
func TestMinHTLC(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()
// We set Alice's MinHTLC to 0.1 BTC. We will attempt to send an
// HTLC BELOW this value to trigger the ErrBelowMinHTLC error.
minValue := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin)
// Setting the min value in Alice's local config means that the
// remote will not accept any HTLCs of value less than specified.
aliceChannel.localChanCfg.MinHTLC = minValue
bobChannel.remoteChanCfg.MinHTLC = minValue
// First, we will add an HTLC of 0.5 BTC. This will not trigger
// ErrBelowMinHTLC.
htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin)
htlc, _ := createHTLC(0, htlcAmt)
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// We add an HTLC below the min value, this should result in
// an ErrBelowMinHTLC error.
amt := minValue - 100
htlc, _ = createHTLC(1, amt)
_, err = aliceChannel.AddHTLC(htlc)
if err != ErrBelowMinHTLC {
t.Fatalf("expected ErrBelowMinHTLC, instead received: %v", err)
}
// Bob will receive this HTLC, but reject the next state update, since
// the htlc is too small.
_, err = bobChannel.ReceiveHTLC(htlc)
if err != nil {
t.Fatalf("error receiving htlc: %v", err)
}
err = forceStateTransition(aliceChannel, bobChannel)
if err != ErrBelowMinHTLC {
t.Fatalf("expected ErrBelowMinHTLC, instead received: %v", err)
}
}