Skip to content

Commit 44db38d

Browse files
committed
lnwire+channeldb: parse inbound fees
In this commit, the tlv extension of a channel update message is parsed. If an inbound fee schedule is encountered, it is reported in the graph rpc calls.
1 parent c32edbd commit 44db38d

File tree

9 files changed

+1551
-1387
lines changed

9 files changed

+1551
-1387
lines changed

channeldb/models/channel_edge_policy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ type ChannelEdgePolicy struct {
7070
// properly validate the set of signatures that cover these new fields,
7171
// and ensure we're able to make upgrades to the network in a forwards
7272
// compatible manner.
73-
ExtraOpaqueData []byte
73+
ExtraOpaqueData lnwire.ExtraOpaqueData
7474
}
7575

7676
// Signature is a channel announcement signature, which is needed for proper

lnrpc/devrpc/dev.swagger.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@
217217
"format": "byte"
218218
},
219219
"description": "Custom channel update tlv records."
220+
},
221+
"inbound_fee_base_msat": {
222+
"type": "integer",
223+
"format": "int32"
224+
},
225+
"inbound_fee_rate_milli_msat": {
226+
"type": "integer",
227+
"format": "int32"
220228
}
221229
}
222230
},

lnrpc/lightning.pb.go

Lines changed: 1409 additions & 1386 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lnrpc/lightning.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3296,6 +3296,9 @@ message RoutingPolicy {
32963296

32973297
// Custom channel update tlv records.
32983298
map<uint64, bytes> custom_records = 8;
3299+
3300+
int32 inbound_fee_base_msat = 9;
3301+
int32 inbound_fee_rate_milli_msat = 10;
32993302
}
33003303

33013304
/*

lnrpc/lightning.swagger.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6773,6 +6773,14 @@
67736773
"format": "byte"
67746774
},
67756775
"description": "Custom channel update tlv records."
6776+
},
6777+
"inbound_fee_base_msat": {
6778+
"type": "integer",
6779+
"format": "int32"
6780+
},
6781+
"inbound_fee_rate_milli_msat": {
6782+
"type": "integer",
6783+
"format": "int32"
67766784
}
67776785
}
67786786
},

lnwire/typed_fee.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package lnwire
2+
3+
import (
4+
"io"
5+
6+
"github.com/lightningnetwork/lnd/tlv"
7+
)
8+
9+
const (
10+
FeeRecordType tlv.Type = 55555
11+
)
12+
13+
// Fee represents a fee schedule.
14+
type Fee struct {
15+
BaseFee int32
16+
FeeRate int32
17+
}
18+
19+
// Record returns a TLV record that can be used to encode/decode the fee
20+
// type from a given TLV stream.
21+
func (l *Fee) Record() tlv.Record {
22+
return tlv.MakeStaticRecord(
23+
FeeRecordType, l, 8, feeEncoder, feeDecoder, //nolint:gomnd
24+
)
25+
}
26+
27+
// feeEncoder is a custom TLV encoder for the fee record.
28+
func feeEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
29+
v, ok := val.(*Fee)
30+
if !ok {
31+
return tlv.NewTypeForEncodingErr(val, "lnwire.Fee")
32+
}
33+
34+
if err := tlv.EUint32T(w, uint32(v.BaseFee), buf); err != nil {
35+
return err
36+
}
37+
38+
return tlv.EUint32T(w, uint32(v.FeeRate), buf)
39+
}
40+
41+
// feeDecoder is a custom TLV decoder for the fee record.
42+
func feeDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
43+
v, ok := val.(*Fee)
44+
if !ok {
45+
return tlv.NewTypeForDecodingErr(val, "lnwire.Fee", l, 8)
46+
}
47+
48+
var baseFee, feeRate uint32
49+
if err := tlv.DUint32(r, &baseFee, buf, 4); err != nil { //nolint: gomnd,lll
50+
return err
51+
}
52+
if err := tlv.DUint32(r, &feeRate, buf, 4); err != nil { //nolint: gomnd,lll
53+
return err
54+
}
55+
56+
v.FeeRate = int32(feeRate)
57+
v.BaseFee = int32(baseFee)
58+
59+
return nil
60+
}

lnwire/typed_fee_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package lnwire
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestTypedFee(t *testing.T) {
10+
t.Parallel()
11+
12+
t.Run("positive", func(t *testing.T) {
13+
t.Parallel()
14+
15+
testTypedFee(t, Fee{
16+
BaseFee: 10,
17+
FeeRate: 20,
18+
})
19+
})
20+
21+
t.Run("negative", func(t *testing.T) {
22+
t.Parallel()
23+
24+
testTypedFee(t, Fee{
25+
BaseFee: -10,
26+
FeeRate: -20,
27+
})
28+
})
29+
}
30+
31+
func testTypedFee(t *testing.T, fee Fee) { //nolint: thelper
32+
var eob ExtraOpaqueData
33+
require.NoError(t, eob.PackRecords(&fee))
34+
35+
var extractedFee Fee
36+
_, err := eob.ExtractRecords(&extractedFee)
37+
require.NoError(t, err)
38+
39+
require.Equal(t, fee, extractedFee)
40+
}

routing/router.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,6 +2724,7 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate) bool {
27242724
MaxHTLC: msg.HtlcMaximumMsat,
27252725
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
27262726
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
2727+
ExtraOpaqueData: msg.ExtraOpaqueData,
27272728
})
27282729
if err != nil && !IsError(err, ErrIgnored, ErrOutdated) {
27292730
log.Errorf("Unable to apply channel update: %v", err)

rpcserver.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5981,6 +5981,23 @@ func marshalExtraOpaqueData(data []byte) map[uint64][]byte {
59815981
return records
59825982
}
59835983

5984+
// extractInboundFeeSafe tries to extract the inbound fee from the given extra
5985+
// opaque data tlv block. If parsing fails, a zero inbound fee is returned. This
5986+
// function is typically used on unvalidated data coming stored in the database.
5987+
// There is not much we can do other than ignoring errors here.
5988+
func extractInboundFeeSafe(data lnwire.ExtraOpaqueData) lnwire.Fee {
5989+
var inboundFee lnwire.Fee
5990+
5991+
_, err := data.ExtractRecords(&inboundFee)
5992+
if err != nil {
5993+
// Return zero fee. Do not return the inboundFee variable
5994+
// because it may be undefined.
5995+
return lnwire.Fee{}
5996+
}
5997+
5998+
return inboundFee
5999+
}
6000+
59846001
func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
59856002
c1, c2 *models.ChannelEdgePolicy) *lnrpc.ChannelEdge {
59866003

@@ -6030,6 +6047,7 @@ func marshalDBRoutingPolicy(
60306047
disabled := policy.ChannelFlags&lnwire.ChanUpdateDisabled != 0
60316048

60326049
customRecords := marshalExtraOpaqueData(policy.ExtraOpaqueData)
6050+
inboundFee := extractInboundFeeSafe(policy.ExtraOpaqueData)
60336051

60346052
return &lnrpc.RoutingPolicy{
60356053
TimeLockDelta: uint32(policy.TimeLockDelta),
@@ -6040,6 +6058,9 @@ func marshalDBRoutingPolicy(
60406058
Disabled: disabled,
60416059
LastUpdate: uint32(policy.LastUpdate.Unix()),
60426060
CustomRecords: customRecords,
6061+
6062+
InboundFeeBaseMsat: inboundFee.BaseFee,
6063+
InboundFeeRateMilliMsat: inboundFee.FeeRate,
60436064
}
60446065
}
60456066

0 commit comments

Comments
 (0)