Merge pull request #779 from Roasbeef/unsigned-msat-and-fee-fixes

lnwire+lnwallet+htlcswitch: modify lnwire.MilliSatoshi to be unsigned, fix fee related bugs that emerged
This commit is contained in:
Olaoluwa Osuntokun 2018-03-08 22:28:16 -05:00 committed by GitHub
commit 855e85511e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 118 deletions

View File

@ -1729,15 +1729,18 @@ func (l *channelLink) processLockedInHtlcs(
fwdInfo.AmountToForward, fwdInfo.AmountToForward,
) )
// If the amount of the incoming HTLC, minus // If the actual fee is less than our expected
// our expected fee isn't equal to the // fee, then we'll reject this HTLC as it
// forwarding instructions, then either the // didn't provide a sufficient amount of fees,
// values have been tampered with, or the send // or the values have been tampered with, or
// used incorrect/dated information to // the send used incorrect/dated information to
// construct the forwarding information for // construct the forwarding information for
// this hop. In any case, we'll cancel this // this hop. In any case, we'll cancel this
// HTLC. // HTLC.
if pd.Amount-expectedFee < fwdInfo.AmountToForward { actualFee := pd.Amount - fwdInfo.AmountToForward
if pd.Amount < fwdInfo.AmountToForward ||
actualFee < expectedFee {
log.Errorf("Incoming htlc(%x) has "+ log.Errorf("Incoming htlc(%x) has "+
"insufficient fee: expected "+ "insufficient fee: expected "+
"%v, got %v", pd.RHash[:], "%v, got %v", pd.RHash[:],

View File

@ -777,7 +777,7 @@ func TestUpdateForwardingPolicy(t *testing.T) {
testStartingHeight, testStartingHeight,
n.firstBobChannelLink, n.carolChannelLink) n.firstBobChannelLink, n.carolChannelLink)
// First, send this 1 BTC payment over the three hops, the payment // First, send this 10 mSAT payment over the three hops, the payment
// should succeed, and all balances should be updated accordingly. // should succeed, and all balances should be updated accordingly.
payResp, err := n.makePayment(n.aliceServer, n.carolServer, payResp, err := n.makePayment(n.aliceServer, n.carolServer,
n.bobServer.PubKey(), hops, amountNoFee, htlcAmt, n.bobServer.PubKey(), hops, amountNoFee, htlcAmt,
@ -827,7 +827,7 @@ func TestUpdateForwardingPolicy(t *testing.T) {
n.firstBobChannelLink.UpdateForwardingPolicy(newPolicy) n.firstBobChannelLink.UpdateForwardingPolicy(newPolicy)
// Next, we'll send the payment again, using the exact same per-hop // Next, we'll send the payment again, using the exact same per-hop
// payload for each node. This payment should fail as it wont' factor // payload for each node. This payment should fail as it won't factor
// in Bob's new fee policy. // in Bob's new fee policy.
_, err = n.makePayment(n.aliceServer, n.carolServer, _, err = n.makePayment(n.aliceServer, n.carolServer,
n.bobServer.PubKey(), hops, amountNoFee, htlcAmt, n.bobServer.PubKey(), hops, amountNoFee, htlcAmt,

View File

@ -2139,14 +2139,24 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment,
// With the weight known, we can now calculate the commitment fee, // With the weight known, we can now calculate the commitment fee,
// ensuring that we account for any dust outputs trimmed above. // ensuring that we account for any dust outputs trimmed above.
commitFee := c.feePerKw.FeeForWeight(totalCommitWeight) commitFee := c.feePerKw.FeeForWeight(totalCommitWeight)
commitFeeMSat := lnwire.NewMSatFromSatoshis(commitFee)
// Currently, within the protocol, the initiator always pays the fees. // Currently, within the protocol, the initiator always pays the fees.
// So we'll subtract the fee amount from the balance of the current // So we'll subtract the fee amount from the balance of the current
// initiator. // initiator. If the initiator is unable to pay the fee fully, then
if lc.channelState.IsInitiator { // their entire output is consumed.
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee) switch {
} else if !lc.channelState.IsInitiator { case lc.channelState.IsInitiator && commitFee > ourBalance.ToSatoshis():
theirBalance -= lnwire.NewMSatFromSatoshis(commitFee) ourBalance = 0
case lc.channelState.IsInitiator:
ourBalance -= commitFeeMSat
case !lc.channelState.IsInitiator && commitFee > theirBalance.ToSatoshis():
theirBalance = 0
case !lc.channelState.IsInitiator:
theirBalance -= commitFeeMSat
} }
var ( var (
@ -3042,14 +3052,14 @@ func (lc *LightningChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
}, nil }, nil
} }
// computeView takes the given htlcView, and calculates the balances, // computeView takes the given htlcView, and calculates the balances, filtered
// filtered view (settling unsettled HTLCs), commitment weight and // view (settling unsettled HTLCs), commitment weight and feePerKw, after
// feePerKw, after applying the HTLCs to the latest commitment. The // applying the HTLCs to the latest commitment. The returned balances are the
// returned balanced are the balances *before* subtracting the // balances *before* subtracting the commitment fee from the initiator's
// commitment fee from the initiator's balance. // balance.
// //
// If the updateState boolean is set true, the add and remove heights // If the updateState boolean is set true, the add and remove heights of the
// of the HTLCs will be set to the next commitment height. // HTLCs will be set to the next commitment height.
func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool, func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
updateState bool) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, int64, updateState bool) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, int64,
*htlcView, SatPerKWeight) { *htlcView, SatPerKWeight) {
@ -3061,16 +3071,16 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
dustLimit = lc.remoteChanCfg.DustLimit dustLimit = lc.remoteChanCfg.DustLimit
} }
// Since the fetched htlc view will include all updates added // Since the fetched htlc view will include all updates added after the
// after the last committed state, we start with the balances // last committed state, we start with the balances reflecting that
// reflecting that state. // state.
ourBalance := commitChain.tip().ourBalance ourBalance := commitChain.tip().ourBalance
theirBalance := commitChain.tip().theirBalance theirBalance := commitChain.tip().theirBalance
// Add the fee from the previous commitment state back to the // Add the fee from the previous commitment state back to the
// initiator's balance, so that the fee can be recalculated and // initiator's balance, so that the fee can be recalculated and
// re-applied in case fee estimation parameters have changed or // re-applied in case fee estimation parameters have changed or the
// the number of outstanding HTLCs has changed. // number of outstanding HTLCs has changed.
if lc.channelState.IsInitiator { if lc.channelState.IsInitiator {
ourBalance += lnwire.NewMSatFromSatoshis( ourBalance += lnwire.NewMSatFromSatoshis(
commitChain.tip().fee) commitChain.tip().fee)
@ -3080,11 +3090,10 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
} }
nextHeight := commitChain.tip().height + 1 nextHeight := commitChain.tip().height + 1
// We evaluate the view at this stage, meaning settled and // We evaluate the view at this stage, meaning settled and failed HTLCs
// failed HTLCs will remove their corresponding added HTLCs. // will remove their corresponding added HTLCs. The resulting filtered
// The resulting filtered view will only have Add entries left, // view will only have Add entries left, making it easy to compare the
// making it easy to compare the channel constraints to the // channel constraints to the final commitment state.
// final commitment state.
filteredHTLCView := lc.evaluateHTLCView(view, &ourBalance, filteredHTLCView := lc.evaluateHTLCView(view, &ourBalance,
&theirBalance, nextHeight, remoteChain, updateState) &theirBalance, nextHeight, remoteChain, updateState)
@ -3155,6 +3164,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
ourLogCounter uint64, remoteChain bool, ourLogCounter uint64, remoteChain bool,
predictAdded *PaymentDescriptor) error { predictAdded *PaymentDescriptor) error {
// Fetch all updates not committed. // Fetch all updates not committed.
view := lc.fetchHTLCView(theirLogCounter, ourLogCounter) view := lc.fetchHTLCView(theirLogCounter, ourLogCounter)
@ -3162,8 +3172,8 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// update log, in order to validate the sanity of the commitment // update log, in order to validate the sanity of the commitment
// resulting from _actually adding_ this HTLC to the state. // resulting from _actually adding_ this HTLC to the state.
if predictAdded != nil { if predictAdded != nil {
// If we are adding an HTLC, this will be an Add to the // If we are adding an HTLC, this will be an Add to the local
// local update log. // update log.
view.ourUpdates = append(view.ourUpdates, predictAdded) view.ourUpdates = append(view.ourUpdates, predictAdded)
} }
@ -3174,21 +3184,33 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
ourInitialBalance := commitChain.tip().ourBalance ourInitialBalance := commitChain.tip().ourBalance
theirInitialBalance := commitChain.tip().theirBalance theirInitialBalance := commitChain.tip().theirBalance
ourBalance, theirBalance, commitWeight, filteredView, feePerKw := ourBalance, theirBalance, commitWeight, filteredView, feePerKw := lc.computeView(
lc.computeView(view, remoteChain, false) view, remoteChain, false,
)
// Calculate the commitment fee, and subtract it from the // Calculate the commitment fee, and subtract it from the initiator's
// initiator's balance. // balance.
commitFee := feePerKw.FeeForWeight(commitWeight) commitFee := feePerKw.FeeForWeight(commitWeight)
commitFeeMsat := lnwire.NewMSatFromSatoshis(commitFee)
if lc.channelState.IsInitiator { if lc.channelState.IsInitiator {
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee) ourBalance -= commitFeeMsat
} else { } else {
theirBalance -= lnwire.NewMSatFromSatoshis(commitFee) theirBalance -= commitFeeMsat
} }
// If the added HTLCs will decrease the balance, make sure // As a quick sanity check, we'll ensure that if we interpret the
// they won't dip the local and remote balances below the // balances as signed integers, they haven't dipped down below zero. If
// channel reserves. // they have, then this indicates that a party doesn't have sufficient
// balance to satisfy the final evaluated HTLC's.
switch {
case int64(ourBalance) < 0:
return ErrBelowChanReserve
case int64(theirBalance) < 0:
return ErrBelowChanReserve
}
// If the added HTLCs will decrease the balance, make sure they won't
// dip the local and remote balances below the channel reserves.
if ourBalance < ourInitialBalance && if ourBalance < ourInitialBalance &&
ourBalance < lnwire.NewMSatFromSatoshis( ourBalance < lnwire.NewMSatFromSatoshis(
lc.localChanCfg.ChanReserve) { lc.localChanCfg.ChanReserve) {
@ -3201,62 +3223,63 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
return ErrBelowChanReserve return ErrBelowChanReserve
} }
// validateUpdates take a set of updates, and validates them // validateUpdates take a set of updates, and validates them against
// against the passed channel constraints. // the passed channel constraints.
validateUpdates := func(updates []*PaymentDescriptor, validateUpdates := func(updates []*PaymentDescriptor,
constraints *channeldb.ChannelConfig) error { constraints *channeldb.ChannelConfig) error {
// We keep track of the number of HTLCs in flight for // We keep track of the number of HTLCs in flight for the
// the commitment, and the amount in flight. // commitment, and the amount in flight.
var numInFlight uint16 var numInFlight uint16
var amtInFlight lnwire.MilliSatoshi var amtInFlight lnwire.MilliSatoshi
// Go through all updates, checking that they don't // Go through all updates, checking that they don't violate the
// violate the channel constraints. // channel constraints.
for _, entry := range updates { for _, entry := range updates {
if entry.EntryType == Add { if entry.EntryType == Add {
// An HTLC is being added, this will // An HTLC is being added, this will add to the
// add to the number and amount in // number and amount in flight.
// flight.
amtInFlight += entry.Amount amtInFlight += entry.Amount
numInFlight++ numInFlight++
// Check that the value of the HTLC they // Check that the value of the HTLC they added
// added is above our minimum. // is above our minimum.
if entry.Amount < constraints.MinHTLC { if entry.Amount < constraints.MinHTLC {
return ErrBelowMinHTLC return ErrBelowMinHTLC
} }
} }
} }
// Now that we know the total value of added HTLCs, // Now that we know the total value of added HTLCs, we check
// we check that this satisfy the MaxPendingAmont // that this satisfy the MaxPendingAmont contraint.
// contraint.
if amtInFlight > constraints.MaxPendingAmount { if amtInFlight > constraints.MaxPendingAmount {
return ErrMaxPendingAmount return ErrMaxPendingAmount
} }
// In this step, we verify that the total number of // In this step, we verify that the total number of active
// active HTLCs does not exceed the constraint of the // HTLCs does not exceed the constraint of the maximum number
// maximum number of HTLCs in flight. // of HTLCs in flight.
if numInFlight > constraints.MaxAcceptedHtlcs { if numInFlight > constraints.MaxAcceptedHtlcs {
return ErrMaxHTLCNumber return ErrMaxHTLCNumber
} }
return nil return nil
} }
// First check that the remote updates won't violate it's // First check that the remote updates won't violate it's channel
// channel constraints. // constraints.
err := validateUpdates(filteredView.theirUpdates, err := validateUpdates(
lc.remoteChanCfg) filteredView.theirUpdates, lc.remoteChanCfg,
)
if err != nil { if err != nil {
return err return err
} }
// Secondly check that our updates won't violate our // Secondly check that our updates won't violate our channel
// channel constraints. // constraints.
err = validateUpdates(filteredView.ourUpdates, err = validateUpdates(
lc.localChanCfg) filteredView.ourUpdates, lc.localChanCfg,
)
if err != nil { if err != nil {
return err return err
} }
@ -3815,11 +3838,13 @@ func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, error)
OnionBlob: htlc.OnionBlob[:], OnionBlob: htlc.OnionBlob[:],
} }
// Make sure adding this HTLC won't violate any of the constrainst // Make sure adding this HTLC won't violate any of the constraints we
// we must keep on our commitment transaction. // must keep on our commitment transaction.
remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex
if err := lc.validateCommitmentSanity(remoteACKedIndex, err := lc.validateCommitmentSanity(
lc.localUpdateLog.logIndex, true, pd); err != nil { remoteACKedIndex, lc.localUpdateLog.logIndex, true, pd,
)
if err != nil {
return 0, err return 0, err
} }
@ -5112,10 +5137,18 @@ func (lc *LightningChannel) validateFeeRate(feePerKw SatPerKWeight) error {
newFee := lnwire.NewMSatFromSatoshis( newFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(txWeight), feePerKw.FeeForWeight(txWeight),
) )
balanceAfterFee := availableBalance - newFee
// If the total fee exceeds our available balance, then we'll reject
// this update as it would mean we need to trim our entire output.
if newFee > availableBalance {
return fmt.Errorf("cannot apply fee_update=%v sat/kw, new fee "+
"of %v is greater than balance of %v", int64(feePerKw),
newFee, availableBalance)
}
// If this new balance is below our reserve, then we can't accommodate // If this new balance is below our reserve, then we can't accommodate
// the fee change, so we'll reject it. // the fee change, so we'll reject it.
balanceAfterFee := availableBalance - newFee
if balanceAfterFee.ToSatoshis() < lc.channelState.LocalChanCfg.ChanReserve { if balanceAfterFee.ToSatoshis() < lc.channelState.LocalChanCfg.ChanReserve {
return fmt.Errorf("cannot apply fee_update=%v sat/kw, "+ return fmt.Errorf("cannot apply fee_update=%v sat/kw, "+
"insufficient balance: start=%v, end=%v", "insufficient balance: start=%v, end=%v",

View File

@ -2462,8 +2462,8 @@ func TestAddHTLCNegativeBalance(t *testing.T) {
} }
defer cleanUp() defer cleanUp()
// We set the channel reserve to 0, such that we can add HTLCs // We set the channel reserve to 0, such that we can add HTLCs all the
// all the way to a negative balance. // way to a negative balance.
aliceChannel.localChanCfg.ChanReserve = 0 aliceChannel.localChanCfg.ChanReserve = 0
// First, we'll add 3 HTLCs of 1 BTC each to Alice's commitment. // First, we'll add 3 HTLCs of 1 BTC each to Alice's commitment.
@ -2476,14 +2476,15 @@ func TestAddHTLCNegativeBalance(t *testing.T) {
} }
} }
// Alice now has an available balance of 2 BTC. We'll add a new HTLC // Alice now has an available balance of 2 BTC. We'll add a new HTLC of
// of value 2 BTC, which should make Alice's balance negative (since // value 2 BTC, which should make Alice's balance negative (since she
// (she has to pay a commitment fee). // has to pay a commitment fee).
htlcAmt = lnwire.NewMSatFromSatoshis(2 * btcutil.SatoshiPerBitcoin) htlcAmt = lnwire.NewMSatFromSatoshis(2 * btcutil.SatoshiPerBitcoin)
htlc, _ := createHTLC(numHTLCs+1, htlcAmt) htlc, _ := createHTLC(numHTLCs+1, htlcAmt)
_, err = aliceChannel.AddHTLC(htlc) _, err = aliceChannel.AddHTLC(htlc)
if err != ErrBelowChanReserve { if err != ErrBelowChanReserve {
t.Fatalf("expected balance below channel reserve, instead got: %v", err) t.Fatalf("expected balance below channel reserve, instead "+
"got: %v", err)
} }
} }
@ -4375,23 +4376,22 @@ func TestDesyncHTLCs(t *testing.T) {
t.Fatalf("unable to recv htlc cancel: %v", err) t.Fatalf("unable to recv htlc cancel: %v", err)
} }
// Alice now has gotten all here original balance (5 BTC) back, // Alice now has gotten all her original balance (5 BTC) back, however,
// however, adding a new HTLC at this point SHOULD fail, since // adding a new HTLC at this point SHOULD fail, since if she adds the
// if she add the HTLC and sign the next state, Bob cannot assume // HTLC and signs the next state, Bob cannot assume she received the
// she received the FailHTLC, and must assume she doesn't have // FailHTLC, and must assume she doesn't have the necessary balance
// the necessary balance available. // available.
// //
// We try adding an HTLC of value 1 BTC, which should fail // We try adding an HTLC of value 1 BTC, which should fail because the
// because the balance is unavailable. // balance is unavailable.
htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin) htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin)
htlc, _ = createHTLC(1, htlcAmt) htlc, _ = createHTLC(1, htlcAmt)
if _, err = aliceChannel.AddHTLC(htlc); err != ErrBelowChanReserve { if _, err = aliceChannel.AddHTLC(htlc); err != ErrBelowChanReserve {
t.Fatalf("expected ErrInsufficientBalance, instead received: %v", t.Fatalf("expected ErrBelowChanReserve, instead received: %v", err)
err)
} }
// Now do a state transition, which will ACK the FailHTLC, making // Now do a state transition, which will ACK the FailHTLC, making Alice
// Alice able to add the new HTLC. // able to add the new HTLC.
if err := forceStateTransition(aliceChannel, bobChannel); err != nil { if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
t.Fatalf("unable to complete state update: %v", err) t.Fatalf("unable to complete state update: %v", err)
} }
@ -4403,7 +4403,8 @@ 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) // TestMaxAcceptedHTLCs tests that the correct error message (ErrMaxHTLCNumber)
// is thrown when a node tries to accept more than MaxAcceptedHTLCs in a channel. // is thrown when a node tries to accept more than MaxAcceptedHTLCs in a
// channel.
func TestMaxAcceptedHTLCs(t *testing.T) { func TestMaxAcceptedHTLCs(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -157,6 +157,15 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
ourBalance = pushMSat ourBalance = pushMSat
theirBalance = capacityMSat - feeMSat - pushMSat theirBalance = capacityMSat - feeMSat - pushMSat
initiator = false initiator = false
// If the responder doesn't have enough funds to actually pay
// the fees, then we'll bail our early.
if int64(theirBalance) < 0 {
return nil, ErrFunderBalanceDust(
int64(commitFee), int64(theirBalance.ToSatoshis()),
int64(2*DefaultDustLimit()),
)
}
} else { } else {
// TODO(roasbeef): need to rework fee structure in general and // TODO(roasbeef): need to rework fee structure in general and
// also when we "unlock" dual funder within the daemon // also when we "unlock" dual funder within the daemon
@ -177,6 +186,15 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
} }
initiator = true initiator = true
// If we, the initiator don't have enough funds to actually pay
// the fees, then we'll exit with an error.
if int64(ourBalance) < 0 {
return nil, ErrFunderBalanceDust(
int64(commitFee), int64(ourBalance),
int64(2*DefaultDustLimit()),
)
}
} }
// If we're the initiator and our starting balance within the channel // If we're the initiator and our starting balance within the channel
@ -288,40 +306,36 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16,
return ErrCsvDelayTooLarge(csvDelay, maxDelay) return ErrCsvDelayTooLarge(csvDelay, maxDelay)
} }
// Fail if we consider the channel reserve to be too large. // Fail if we consider the channel reserve to be too large. We
// We currently fail if it is greater than 20% of the // currently fail if it is greater than 20% of the channel capacity.
// channel capacity.
maxChanReserve := r.partialState.Capacity / 5 maxChanReserve := r.partialState.Capacity / 5
if chanReserve > maxChanReserve { if chanReserve > maxChanReserve {
return ErrChanReserveTooLarge(chanReserve, maxChanReserve) return ErrChanReserveTooLarge(chanReserve, maxChanReserve)
} }
// Fail if the minimum HTLC value is too large. If this is // Fail if the minimum HTLC value is too large. If this is too large,
// too large, the channel won't be useful for sending small // the channel won't be useful for sending small payments. This limit
// payments. This limit is currently set to maxValueInFlight, // is currently set to maxValueInFlight, effectively letting the remote
// effictively letting the remote setting this as large as // setting this as large as it wants.
// it wants.
// TODO(halseth): set a reasonable/dynamic value.
if minHtlc > maxValueInFlight { if minHtlc > maxValueInFlight {
return ErrMinHtlcTooLarge(minHtlc, maxValueInFlight) return ErrMinHtlcTooLarge(minHtlc, maxValueInFlight)
} }
// Fail if maxHtlcs is above the maximum allowed number of 483. // Fail if maxHtlcs is above the maximum allowed number of 483. This
// This number is specified in BOLT-02. // number is specified in BOLT-02.
if maxHtlcs > uint16(MaxHTLCNumber/2) { if maxHtlcs > uint16(MaxHTLCNumber/2) {
return ErrMaxHtlcNumTooLarge(maxHtlcs, uint16(MaxHTLCNumber/2)) return ErrMaxHtlcNumTooLarge(maxHtlcs, uint16(MaxHTLCNumber/2))
} }
// Fail if we consider maxHtlcs too small. If this is too small // Fail if we consider maxHtlcs too small. If this is too small we
// we cannot offer many HTLCs to the remote. // cannot offer many HTLCs to the remote.
const minNumHtlc = 5 const minNumHtlc = 5
if maxHtlcs < minNumHtlc { if maxHtlcs < minNumHtlc {
return ErrMaxHtlcNumTooSmall(maxHtlcs, minNumHtlc) return ErrMaxHtlcNumTooSmall(maxHtlcs, minNumHtlc)
} }
// Fail if we consider maxValueInFlight too small. We currently // Fail if we consider maxValueInFlight too small. We currently require
// require the remote to at least allow minNumHtlc * minHtlc // the remote to at least allow minNumHtlc * minHtlc in flight.
// in flight.
if maxValueInFlight < minNumHtlc*minHtlc { if maxValueInFlight < minNumHtlc*minHtlc {
return ErrMaxValueInFlightTooSmall(maxValueInFlight, return ErrMaxValueInFlightTooSmall(maxValueInFlight,
minNumHtlc*minHtlc) minNumHtlc*minHtlc)

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -806,8 +807,9 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
dustLimit: tc.dustLimit, dustLimit: tc.dustLimit,
isOurs: true, isOurs: true,
} }
err = channel.createCommitmentTx(commitmentView, theHTLCView, err = channel.createCommitmentTx(
keys) commitmentView, theHTLCView, keys,
)
if err != nil { if err != nil {
t.Errorf("Case %d: Failed to create new commitment tx: %v", i, err) t.Errorf("Case %d: Failed to create new commitment tx: %v", i, err)
continue continue
@ -836,8 +838,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
// Check that commitment transaction was created correctly. // Check that commitment transaction was created correctly.
if commitTx.WitnessHash() != *expectedCommitmentTx.WitnessHash() { if commitTx.WitnessHash() != *expectedCommitmentTx.WitnessHash() {
t.Errorf("Case %d: Generated unexpected commitment tx: "+ t.Errorf("Case %d: Generated unexpected commitment tx: "+
"expected %s, got %s", i, expectedCommitmentTx.WitnessHash(), "expected %s, got %s", i, spew.Sdump(expectedCommitmentTx),
commitTx.WitnessHash()) spew.Sdump(commitTx))
continue continue
} }

View File

@ -8,7 +8,7 @@ import (
// mSatScale is a value that's used to scale satoshis to milli-satoshis, and // mSatScale is a value that's used to scale satoshis to milli-satoshis, and
// the other way around. // the other way around.
const mSatScale int64 = 1000 const mSatScale uint64 = 1000
// MilliSatoshi are the native unit of the Lightning Network. A milli-satoshi // MilliSatoshi are the native unit of the Lightning Network. A milli-satoshi
// is simply 1/1000th of a satoshi. There are 1000 milli-satoshis in a single // is simply 1/1000th of a satoshi. There are 1000 milli-satoshis in a single
@ -16,12 +16,12 @@ const mSatScale int64 = 1000
// milli-satoshis. As milli-satoshis aren't deliverable on the native // milli-satoshis. As milli-satoshis aren't deliverable on the native
// blockchain, before settling to broadcasting, the values are rounded down to // blockchain, before settling to broadcasting, the values are rounded down to
// the nearest satoshi. // the nearest satoshi.
type MilliSatoshi int64 type MilliSatoshi uint64
// NewMSatFromSatoshis creates a new MilliSatoshi instance from a target amount // NewMSatFromSatoshis creates a new MilliSatoshi instance from a target amount
// of satoshis. // of satoshis.
func NewMSatFromSatoshis(sat btcutil.Amount) MilliSatoshi { func NewMSatFromSatoshis(sat btcutil.Amount) MilliSatoshi {
return MilliSatoshi(int64(sat) * mSatScale) return MilliSatoshi(uint64(sat) * mSatScale)
} }
// ToBTC converts the target MilliSatoshi amount to its corresponding value // ToBTC converts the target MilliSatoshi amount to its corresponding value
@ -34,10 +34,12 @@ func (m MilliSatoshi) ToBTC() float64 {
// ToSatoshis converts the target MilliSatoshi amount to satoshis. Simply, this // ToSatoshis converts the target MilliSatoshi amount to satoshis. Simply, this
// sheds a factor of 1000 from the mSAT amount in order to convert it to SAT. // sheds a factor of 1000 from the mSAT amount in order to convert it to SAT.
func (m MilliSatoshi) ToSatoshis() btcutil.Amount { func (m MilliSatoshi) ToSatoshis() btcutil.Amount {
return btcutil.Amount(int64(m) / mSatScale) return btcutil.Amount(uint64(m) / mSatScale)
} }
// String returns the string representation of the mSAT amount. // String returns the string representation of the mSAT amount.
func (m MilliSatoshi) String() string { func (m MilliSatoshi) String() string {
return fmt.Sprintf("%v mSAT", int64(m)) return fmt.Sprintf("%v mSAT", uint64(m))
} }
// TODO(roasbeef): extend with arithmetic operations?

View File

@ -149,10 +149,6 @@ func TestEncodeAmount(t *testing.T) {
valid bool valid bool
result string result string
}{ }{
{
msat: -10, // mSat
valid: false, // negative amount
},
{ {
msat: 1, // mSat msat: 1, // mSat
valid: true, valid: true,