lnwallet: reject funding flows if local amount is insufficient w.r.t fees

This commit is contained in:
Olaoluwa Osuntokun 2017-11-26 13:32:57 -06:00
parent c986e52da7
commit 24ad3e17de
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
3 changed files with 51 additions and 5 deletions

View File

@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
@ -501,9 +502,12 @@ func testCancelNonExistantReservation(miner *rpctest.Harness,
}
// Create our own reservation, give it some ID.
res := lnwallet.NewChannelReservation(
res, err := lnwallet.NewChannelReservation(
1000, 1000, feeRate, alice, 22, 10, &testHdSeed,
)
if err != nil {
t.Fatalf("unable to create res: %v", err)
}
// Attempt to cancel this reservation. This should fail, we know
// nothing of it.
@ -512,6 +516,28 @@ func testCancelNonExistantReservation(miner *rpctest.Harness,
}
}
func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
alice, _ *lnwallet.LightningWallet, t *testing.T) {
// We'll attempt to create a new reservation with an extremely high fee
// rate. This should push our balance into the negative and result in a
// failure to create the reservation.
fundingAmount := btcutil.Amount(4 * 1e8)
feePerKw := btcutil.Amount(btcutil.SatoshiPerBitcoin * 10)
_, err := alice.InitChannelReservation(
fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub,
bobAddr, chainHash,
)
switch {
case err == nil:
t.Fatalf("initialization should've failed due to " +
"insufficient local amount")
case !strings.Contains(err.Error(), "local output is too small"):
t.Fatalf("incorrect error: %v", err)
}
}
func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContribution) {
_, _, line, _ := runtime.Caller(1)
@ -1276,6 +1302,10 @@ type walletTestCase struct {
}
var walletTests = []walletTestCase{
{
name: "insane fee reject",
test: testReservationInitiatorBalanceBelowDustCancel,
},
{
name: "single funding workflow",
test: testSingleFunderReservationWorkflow,

View File

@ -1,6 +1,7 @@
package lnwallet
import (
"fmt"
"net"
"sync"
@ -136,7 +137,7 @@ type ChannelReservation struct {
// lnwallet.InitChannelReservation interface.
func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
wallet *LightningWallet, id uint64, pushMSat lnwire.MilliSatoshi,
chainHash *chainhash.Hash) *ChannelReservation {
chainHash *chainhash.Hash) (*ChannelReservation, error) {
var (
ourBalance lnwire.MilliSatoshi
@ -181,6 +182,17 @@ func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
initiator = true
}
// If we're the initiator and our starting balance within the channel
// after we take account of fees is below dust, then we'll reject this
// channel creation request.
//
// TODO(roasbeef): reject if 30% goes to fees? dust channel
if initiator && ourBalance.ToSatoshis() <= DefaultDustLimit() {
return nil, fmt.Errorf("unable to init reservation, with "+
"fee=%v sat/kw, local output is too small: %v sat",
int64(commitFee), int64(ourBalance.ToSatoshis()))
}
// Next we'll set the channel type based on what we can ascertain about
// the balances/push amount within the channel.
var chanType channeldb.ChannelType
@ -231,7 +243,7 @@ func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
chanOpen: make(chan *openChanDetails, 1),
chanOpenErr: make(chan error, 1),
wallet: wallet,
}
}, nil
}
// SetNumConfsRequired sets the number of confirmations that are required for

View File

@ -492,8 +492,13 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
}
id := atomic.AddUint64(&l.nextFundingID, 1)
reservation := NewChannelReservation(req.capacity, req.fundingAmount,
reservation, err := NewChannelReservation(req.capacity, req.fundingAmount,
req.commitFeePerKw, l, id, req.pushMSat, l.Cfg.NetParams.GenesisHash)
if err != nil {
req.err <- err
req.resp <- nil
return
}
// Grab the mutex on the ChannelReservation to ensure thread-safety
reservation.Lock()
@ -523,7 +528,6 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
// for the duration of the channel. The keys include: our multi-sig
// key, the base revocation key, the base htlc key,the base payment
// key, and the delayed payment key.
var err error
reservation.ourContribution.MultiSigKey, err = l.NewRawKey()
if err != nil {
req.err <- err