Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

htlcswitch: add p2p inbound fee #6878

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@ type channelLink struct {
// log is a link-specific logging instance.
log btclog.Logger

ourInboundBaseFee int32
ourInboundFeeRate int32

theirInboundBaseFee int32
theirInboundFeeRate int32

wg sync.WaitGroup
quit chan struct{}
}
Expand Down Expand Up @@ -1127,6 +1133,18 @@ func (l *channelLink) htlcManager() {
go l.fwdPkgGarbager()
}

l.ourInboundBaseFee = -10000
l.ourInboundFeeRate = -20000

err := l.cfg.Peer.SendMessage(false, &lnwire.UpdateInboundFee{
ChanID: l.ChanID(),
BaseFee: l.ourInboundBaseFee,
FeeRate: l.ourInboundFeeRate,
})
if err != nil {
l.log.Warnf("unable to send UpdateInboundFee: %v", err)
}

for {
// We must always check if we failed at some point processing
// the last update before processing the next.
Expand Down Expand Up @@ -1416,6 +1434,14 @@ func (l *channelLink) handleDownstreamUpdateAdd(pkt *htlcPacket) error {
return errors.New("not an UpdateAddHTLC packet")
}

inboundFee := lnwire.MilliSatoshi(l.calculateInboundFee(int64(pkt.amount)))

l.log.Infof("Adding inbound fee to htlc amount: fee=%v, baseFee=%v, feeRate=%v",
int64(inboundFee), l.theirInboundBaseFee, l.theirInboundFeeRate)

htlc.Amount += inboundFee
pkt.amount += inboundFee

// If hodl.AddOutgoing mode is active, we exit early to simulate
// arbitrary delays between the switch adding an ADD to the
// mailbox, and the HTLC being added to the commitment state.
Expand Down Expand Up @@ -2090,6 +2116,14 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
"ChannelPoint(%v): received error from peer: %v",
l.channel.ChannelPoint(), msg.Error(),
)

case *lnwire.UpdateInboundFee:
l.log.Infof("Updating inbound fee to %v + %v ppm",
msg.BaseFee, msg.FeeRate)

l.theirInboundBaseFee = msg.BaseFee
l.theirInboundFeeRate = msg.FeeRate

default:
l.log.Warnf("received unknown message of type %T", msg)
}
Expand Down Expand Up @@ -2995,6 +3029,12 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,

fwdInfo := pld.ForwardingInfo()

// Subtract inbound fee.
modifiedPd := *pd
inboundFee := lnwire.MilliSatoshi(l.calculateInboundFeeFromIncoming(int64(pd.Amount)))
modifiedPd.Amount -= inboundFee
pd = &modifiedPd

switch fwdInfo.NextHop {
case hop.Exit:
err := l.processExitHop(
Expand Down Expand Up @@ -3162,6 +3202,43 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
l.forwardBatch(replay, switchPackets...)
}

func calculateInboundFee(
amt int64, baseFee, feeRate int32) int64 {

return int64(baseFee) +
int64(feeRate)*amt/1e6
}

func (l *channelLink) calculateInboundFee(outgoingAmt int64) int64 {
return calculateInboundFee(
outgoingAmt, l.theirInboundBaseFee, l.theirInboundFeeRate,
)
}

// divideCeil divides dividend by factor and rounds the result up.
func divideCeil(dividend, factor int64) int64 {
return (dividend + factor - 1) / factor
}

func calculateInboundFeeFromIncoming(
incomingAmt int64, baseFee, feeRate int32) int64 {

return incomingAmt - divideCeil(
1e6*(incomingAmt-int64(baseFee)),
1e6+int64(feeRate),
)
}

// ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
// amount.
func (l *channelLink) calculateInboundFeeFromIncoming(
incomingAmt int64) int64 {

return calculateInboundFeeFromIncoming(
incomingAmt, l.ourInboundBaseFee, l.ourInboundFeeRate,
)
}

// processExitHop handles an htlc for which this link is the exit hop. It
// returns a boolean indicating whether the commitment tx needs an update.
func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
Expand Down
14 changes: 14 additions & 0 deletions htlcswitch/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6649,3 +6649,17 @@ func assertFailureCode(t *testing.T, err error, code lnwire.FailCode) {
code, rtErr.WireMessage().Code())
}
}

func TestCalculateInboundFee(t *testing.T) {
const (
amt = 10000000
feeBase = -10000
feeRate = -20000
)

fee := calculateInboundFee(amt, feeBase, feeRate)
require.Equal(t, -210000, int(fee))

fee = calculateInboundFeeFromIncoming(amt+fee, feeBase, feeRate)
require.Equal(t, -210000, int(fee))
}
4 changes: 4 additions & 0 deletions lnwallet/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5366,6 +5366,8 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, err
return 0, err
}

lc.log.Infof("DEBUG: receiving amount %v", htlc.Amount)

lc.remoteUpdateLog.appendHtlc(pd)

return pd.HtlcIndex, nil
Expand Down Expand Up @@ -5407,6 +5409,8 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte,
return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex}
}

lc.log.Infof("DEBUG: settling amount %v", htlc.Amount)

// Now that we know the HTLC exists, before checking to see if the
// preimage matches, we'll ensure that we haven't already attempted to
// modify the HTLC.
Expand Down
5 changes: 5 additions & 0 deletions lnwire/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
MsgUpdateFee = 134
MsgUpdateFailMalformedHTLC = 135
MsgChannelReestablish = 136
MsgUpdateInboundFee = 32768
MsgChannelAnnouncement = 256
MsgNodeAnnouncement = 257
MsgChannelUpdate = 258
Expand Down Expand Up @@ -96,6 +97,8 @@ func (t MessageType) String() string {
return "ClosingSigned"
case MsgUpdateAddHTLC:
return "UpdateAddHTLC"
case MsgUpdateInboundFee:
return "UpdateInboundFee"
case MsgUpdateFailHTLC:
return "UpdateFailHTLC"
case MsgUpdateFulfillHTLC:
Expand Down Expand Up @@ -198,6 +201,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
msg = &ClosingSigned{}
case MsgUpdateAddHTLC:
msg = &UpdateAddHTLC{}
case MsgUpdateInboundFee:
msg = &UpdateInboundFee{}
case MsgUpdateFailHTLC:
msg = &UpdateFailHTLC{}
case MsgUpdateFulfillHTLC:
Expand Down
86 changes: 86 additions & 0 deletions lnwire/update_inbound_fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package lnwire

import (
"bytes"
"io"
)

type UpdateInboundFee struct {
// ChanID is the particular active channel that this
// UpdateInboundDiscount is bound to.
ChanID ChannelID

BaseFee int32

FeeRate int32

ExtraData ExtraOpaqueData
}

// NewUpdateAddHTLC returns a new empty UpdateAddHTLC message.
func NewUpdateInboundDiscount() *UpdateInboundFee {
return &UpdateInboundFee{}
}

// A compile time check to ensure UpdateAddHTLC implements the lnwire.Message
// interface.
var _ Message = (*UpdateInboundFee)(nil)

// Decode deserializes a serialized UpdateAddHTLC message stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *UpdateInboundFee) Decode(r io.Reader, pver uint32) error {
var baseFee, feeRate uint32

err := ReadElements(r,
&c.ChanID,
&baseFee,
&feeRate,
)
if err != nil {
return err
}

c.BaseFee = int32(baseFee)
c.FeeRate = int32(feeRate)

return c.ExtraData.Decode(r)
}

// Encode serializes the target UpdateAddHTLC into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (c *UpdateInboundFee) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, c.ChanID); err != nil {
return err
}

if err := WriteUint32(w, uint32(c.BaseFee)); err != nil {
return err
}

if err := WriteUint32(w, uint32(c.FeeRate)); err != nil {
return err
}

// Finally, append any extra opaque data.
return WriteBytes(w, c.ExtraData)
}

// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (c *UpdateInboundFee) MsgType() MessageType {
return MsgUpdateInboundFee
}

// TargetChanID returns the channel id of the link for which this message is
// intended.
//
// NOTE: Part of peer.LinkUpdater interface.
func (c *UpdateInboundFee) TargetChanID() ChannelID {
return c.ChanID
}