lnwallet test: add channel constraints tests
This commit adds the tests TestMaxAcceptedHTLCs, TestMaxPendingAmount, TestChanReserve and TestMinHTLC.
This commit is contained in:
parent
f9701cde63
commit
82dc8e0794
@ -4378,3 +4378,359 @@ func TestDesyncHTLCs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): testing.Quick test case for retrans!!!
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user