mirror of https://github.com/BTCPrivate/lnd.git
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:
commit
855e85511e
|
@ -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[:],
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue