Merge pull request #9897 from ellemouton/inboundFeeTLV
multi: explicitly define InboundFees in ChannelUpdate and ChannelEdgePolicy
This commit is contained in:
22 files changed
+248
-101
No files matched your search
@@ -3257,6 +3257,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
|
|||||||
MaxHTLC: upd.HtlcMaximumMsat,
|
MaxHTLC: upd.HtlcMaximumMsat,
|
||||||
FeeBaseMSat: lnwire.MilliSatoshi(upd.BaseFee),
|
FeeBaseMSat: lnwire.MilliSatoshi(upd.BaseFee),
|
||||||
FeeProportionalMillionths: lnwire.MilliSatoshi(upd.FeeRate),
|
FeeProportionalMillionths: lnwire.MilliSatoshi(upd.FeeRate),
|
||||||
|
InboundFee: upd.InboundFee.ValOpt(),
|
||||||
ExtraOpaqueData: upd.ExtraOpaqueData,
|
ExtraOpaqueData: upd.ExtraOpaqueData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ circuit. The indices are only available for forwarding events saved after v0.20.
|
|||||||
# Technical and Architectural Updates
|
# Technical and Architectural Updates
|
||||||
## BOLT Spec Updates
|
## BOLT Spec Updates
|
||||||
|
|
||||||
|
* Explicitly define the [inbound fee TLV
|
||||||
|
record](https://github.com/lightningnetwork/lnd/pull/9897) on the
|
||||||
|
`channel_update` message and handle it explicitly throughout the code base
|
||||||
|
instead of extracting it from the TLV stream at various call-sites.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
@@ -99,4 +104,5 @@ circuit. The indices are only available for forwarding events saved after v0.20.
|
|||||||
# Contributors (Alphabetical Order)
|
# Contributors (Alphabetical Order)
|
||||||
|
|
||||||
* Abdulkbk
|
* Abdulkbk
|
||||||
Pins
|
* Elle Mouton
|
||||||
|
* Pins
|
||||||
@@ -203,6 +203,9 @@ require (
|
|||||||
// store have been included in a tagged sqldb version.
|
// store have been included in a tagged sqldb version.
|
||||||
replace github.com/lightningnetwork/lnd/sqldb => ./sqldb
|
replace github.com/lightningnetwork/lnd/sqldb => ./sqldb
|
||||||
|
|
||||||
|
// TODO(elle): replace once the updated tlv package has been tagged.
|
||||||
|
replace github.com/lightningnetwork/lnd/tlv => ./tlv
|
||||||
|
|
||||||
// This replace is for https://github.com/advisories/GHSA-25xm-hr59-7c27
|
// This replace is for https://github.com/advisories/GHSA-25xm-hr59-7c27
|
||||||
replace github.com/ulikunitz/xz => github.com/ulikunitz/xz v0.5.11
|
replace github.com/ulikunitz/xz => github.com/ulikunitz/xz v0.5.11
|
||||||
|
|
||||||
|
|||||||
@@ -377,8 +377,6 @@ github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQ
|
|||||||
github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4=
|
github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4=
|
||||||
github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM=
|
github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM=
|
||||||
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
|
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
|
||||||
github.com/lightningnetwork/lnd/tlv v1.3.1 h1:o7CZg06y+rJZfUMAo0WzBLr0pgBWCzrt0f9gpujYUzk=
|
|
||||||
github.com/lightningnetwork/lnd/tlv v1.3.1/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc=
|
|
||||||
github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE=
|
github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE=
|
||||||
github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI=
|
github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI=
|
||||||
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
|
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
|
||||||
|
|||||||
+5
-2
@@ -943,7 +943,7 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.UpdateEdge(&models.ChannelEdgePolicy{
|
update := &models.ChannelEdgePolicy{
|
||||||
SigBytes: msg.Signature.ToSignatureBytes(),
|
SigBytes: msg.Signature.ToSignatureBytes(),
|
||||||
ChannelID: msg.ShortChannelID.ToUint64(),
|
ChannelID: msg.ShortChannelID.ToUint64(),
|
||||||
LastUpdate: time.Unix(int64(msg.Timestamp), 0),
|
LastUpdate: time.Unix(int64(msg.Timestamp), 0),
|
||||||
@@ -954,8 +954,11 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
|
|||||||
MaxHTLC: msg.HtlcMaximumMsat,
|
MaxHTLC: msg.HtlcMaximumMsat,
|
||||||
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
|
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
|
||||||
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
|
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
|
||||||
|
InboundFee: msg.InboundFee.ValOpt(),
|
||||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
err = b.UpdateEdge(update)
|
||||||
if err != nil && !IsError(err, ErrIgnored, ErrOutdated) {
|
if err != nil && !IsError(err, ErrIgnored, ErrOutdated) {
|
||||||
log.Errorf("Unable to apply channel update: %v", err)
|
log.Errorf("Unable to apply channel update: %v", err)
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ var (
|
|||||||
ErrEdgePolicyOptionalFieldNotFound = fmt.Errorf("optional field not " +
|
ErrEdgePolicyOptionalFieldNotFound = fmt.Errorf("optional field not " +
|
||||||
"present")
|
"present")
|
||||||
|
|
||||||
|
// ErrParsingExtraTLVBytes is returned when we attempt to parse
|
||||||
|
// extra opaque bytes as a TLV stream, but the parsing fails.
|
||||||
|
ErrParsingExtraTLVBytes = fmt.Errorf("error parsing extra TLV bytes")
|
||||||
|
|
||||||
// ErrGraphNotFound is returned when at least one of the components of
|
// ErrGraphNotFound is returned when at least one of the components of
|
||||||
// graph doesn't exist.
|
// graph doesn't exist.
|
||||||
ErrGraphNotFound = fmt.Errorf("graph bucket not initialized")
|
ErrGraphNotFound = fmt.Errorf("graph bucket not initialized")
|
||||||
|
|||||||
+6
-13
@@ -180,17 +180,6 @@ func (c *GraphCache) updateOrAddEdge(node route.Vertex, edge *DirectedChannel) {
|
|||||||
func (c *GraphCache) UpdatePolicy(policy *models.ChannelEdgePolicy, fromNode,
|
func (c *GraphCache) UpdatePolicy(policy *models.ChannelEdgePolicy, fromNode,
|
||||||
toNode route.Vertex, edge1 bool) {
|
toNode route.Vertex, edge1 bool) {
|
||||||
|
|
||||||
// Extract inbound fee if possible and available. If there is a decoding
|
|
||||||
// error, ignore this policy.
|
|
||||||
var inboundFee lnwire.Fee
|
|
||||||
_, err := policy.ExtraOpaqueData.ExtractRecords(&inboundFee)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to extract records from edge policy %v: %v",
|
|
||||||
policy.ChannelID, err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.mtx.Lock()
|
c.mtx.Lock()
|
||||||
defer c.mtx.Unlock()
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
@@ -216,13 +205,17 @@ func (c *GraphCache) UpdatePolicy(policy *models.ChannelEdgePolicy, fromNode,
|
|||||||
// policy for node 1.
|
// policy for node 1.
|
||||||
case channel.IsNode1 && edge1:
|
case channel.IsNode1 && edge1:
|
||||||
channel.OutPolicySet = true
|
channel.OutPolicySet = true
|
||||||
channel.InboundFee = inboundFee
|
policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
channel.InboundFee = fee
|
||||||
|
})
|
||||||
|
|
||||||
// This is node 2, and it is edge 2, so this is the outgoing
|
// This is node 2, and it is edge 2, so this is the outgoing
|
||||||
// policy for node 2.
|
// policy for node 2.
|
||||||
case !channel.IsNode1 && !edge1:
|
case !channel.IsNode1 && !edge1:
|
||||||
channel.OutPolicySet = true
|
channel.OutPolicySet = true
|
||||||
channel.InboundFee = inboundFee
|
policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
channel.InboundFee = fee
|
||||||
|
})
|
||||||
|
|
||||||
// The other two cases left mean it's the inbound policy for the
|
// The other two cases left mean it's the inbound policy for the
|
||||||
// node.
|
// node.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
@@ -37,11 +38,17 @@ func TestGraphCacheAddNode(t *testing.T) {
|
|||||||
channelFlagA, channelFlagB = 1, 0
|
channelFlagA, channelFlagB = 1, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inboundFee := lnwire.Fee{
|
||||||
|
BaseFee: 10,
|
||||||
|
FeeRate: 20,
|
||||||
|
}
|
||||||
|
|
||||||
outPolicy1 := &models.ChannelEdgePolicy{
|
outPolicy1 := &models.ChannelEdgePolicy{
|
||||||
ChannelID: 1000,
|
ChannelID: 1000,
|
||||||
ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagA),
|
ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagA),
|
||||||
ToNode: nodeB,
|
ToNode: nodeB,
|
||||||
// Define an inbound fee.
|
// Define an inbound fee.
|
||||||
|
InboundFee: fn.Some(inboundFee),
|
||||||
ExtraOpaqueData: []byte{
|
ExtraOpaqueData: []byte{
|
||||||
253, 217, 3, 8, 0, 0, 0, 10, 0, 0, 0, 20,
|
253, 217, 3, 8, 0, 0, 0, 10, 0, 0, 0, 20,
|
||||||
},
|
},
|
||||||
|
|||||||
+12
-6
@@ -4195,19 +4195,25 @@ func TestGraphCacheForEachNodeChannel(t *testing.T) {
|
|||||||
|
|
||||||
directedChan := getSingleChannel()
|
directedChan := getSingleChannel()
|
||||||
require.NotNil(t, directedChan)
|
require.NotNil(t, directedChan)
|
||||||
require.Equal(t, directedChan.InboundFee, lnwire.Fee{
|
expectedInbound := lnwire.Fee{
|
||||||
BaseFee: 10,
|
BaseFee: 10,
|
||||||
FeeRate: 20,
|
FeeRate: 20,
|
||||||
})
|
}
|
||||||
|
require.Equal(t, expectedInbound, directedChan.InboundFee)
|
||||||
|
|
||||||
// Set an invalid inbound fee and check that the edge is no longer
|
// Set an invalid inbound fee and check that persistence fails.
|
||||||
// returned.
|
|
||||||
edge1.ExtraOpaqueData = []byte{
|
edge1.ExtraOpaqueData = []byte{
|
||||||
253, 217, 3, 8, 0,
|
253, 217, 3, 8, 0,
|
||||||
}
|
}
|
||||||
require.NoError(t, graph.UpdateEdgePolicy(edge1))
|
require.ErrorIs(
|
||||||
|
t, graph.UpdateEdgePolicy(edge1), ErrParsingExtraTLVBytes,
|
||||||
|
)
|
||||||
|
|
||||||
require.Nil(t, getSingleChannel())
|
// Since persistence of the last update failed, we should still bet
|
||||||
|
// the previous result when we query the channel again.
|
||||||
|
directedChan = getSingleChannel()
|
||||||
|
require.NotNil(t, directedChan)
|
||||||
|
require.Equal(t, expectedInbound, directedChan.InboundFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGraphLoading asserts that the cache is properly reconstructed after a
|
// TestGraphLoading asserts that the cache is properly reconstructed after a
|
||||||
|
|||||||
+60
-22
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
"github.com/lightningnetwork/lnd/aliasmgr"
|
"github.com/lightningnetwork/lnd/aliasmgr"
|
||||||
"github.com/lightningnetwork/lnd/batch"
|
"github.com/lightningnetwork/lnd/batch"
|
||||||
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
@@ -284,6 +285,13 @@ func (c *KVStore) getChannelMap(edges kvdb.RBucket) (
|
|||||||
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
|
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
// We don't want a single policy with bad TLV data to stop us
|
||||||
|
// from loading the rest of the data, so we just skip this
|
||||||
|
// policy. This is for backwards compatibility since we did not
|
||||||
|
// use to validate TLV data in the past before persisting it.
|
||||||
|
case errors.Is(err, ErrParsingExtraTLVBytes):
|
||||||
|
return nil
|
||||||
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -474,16 +482,6 @@ func (c *KVStore) forEachNodeDirectedChannel(tx kvdb.RTx,
|
|||||||
cachedInPolicy.ToNodeFeatures = toNodeFeatures
|
cachedInPolicy.ToNodeFeatures = toNodeFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var inboundFee lnwire.Fee
|
|
||||||
if p1 != nil {
|
|
||||||
// Extract inbound fee. If there is a decoding error,
|
|
||||||
// skip this edge.
|
|
||||||
_, err := p1.ExtraOpaqueData.ExtractRecords(&inboundFee)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
directedChannel := &DirectedChannel{
|
directedChannel := &DirectedChannel{
|
||||||
ChannelID: e.ChannelID,
|
ChannelID: e.ChannelID,
|
||||||
IsNode1: node == e.NodeKey1Bytes,
|
IsNode1: node == e.NodeKey1Bytes,
|
||||||
@@ -491,7 +489,12 @@ func (c *KVStore) forEachNodeDirectedChannel(tx kvdb.RTx,
|
|||||||
Capacity: e.Capacity,
|
Capacity: e.Capacity,
|
||||||
OutPolicySet: p1 != nil,
|
OutPolicySet: p1 != nil,
|
||||||
InPolicy: cachedInPolicy,
|
InPolicy: cachedInPolicy,
|
||||||
InboundFee: inboundFee,
|
}
|
||||||
|
|
||||||
|
if p1 != nil {
|
||||||
|
p1.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
directedChannel.InboundFee = fee
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if node == e.NodeKey2Bytes {
|
if node == e.NodeKey2Bytes {
|
||||||
@@ -2342,7 +2345,7 @@ func (c *KVStore) FilterChannelRange(startHeight,
|
|||||||
edge, err := deserializeChanEdgePolicyRaw(r)
|
edge, err := deserializeChanEdgePolicyRaw(r)
|
||||||
if err != nil && !errors.Is(
|
if err != nil && !errors.Is(
|
||||||
err, ErrEdgePolicyOptionalFieldNotFound,
|
err, ErrEdgePolicyOptionalFieldNotFound,
|
||||||
) {
|
) && !errors.Is(err, ErrParsingExtraTLVBytes) {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -2357,7 +2360,7 @@ func (c *KVStore) FilterChannelRange(startHeight,
|
|||||||
edge, err := deserializeChanEdgePolicyRaw(r)
|
edge, err := deserializeChanEdgePolicyRaw(r)
|
||||||
if err != nil && !errors.Is(
|
if err != nil && !errors.Is(
|
||||||
err, ErrEdgePolicyOptionalFieldNotFound,
|
err, ErrEdgePolicyOptionalFieldNotFound,
|
||||||
) {
|
) && !errors.Is(err, ErrParsingExtraTLVBytes) {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -4334,15 +4337,20 @@ func putChanEdgePolicy(edges kvdb.RwBucket, edge *models.ChannelEdgePolicy,
|
|||||||
// need to deserialize the existing policy within the database
|
// need to deserialize the existing policy within the database
|
||||||
// (now outdated by the new one), and delete its corresponding
|
// (now outdated by the new one), and delete its corresponding
|
||||||
// entry within the update index. We'll ignore any
|
// entry within the update index. We'll ignore any
|
||||||
// ErrEdgePolicyOptionalFieldNotFound error, as we only need
|
// ErrEdgePolicyOptionalFieldNotFound or ErrParsingExtraTLVBytes
|
||||||
// the channel ID and update time to delete the entry.
|
// errors, as we only need the channel ID and update time to
|
||||||
|
// delete the entry.
|
||||||
|
//
|
||||||
// TODO(halseth): get rid of these invalid policies in a
|
// TODO(halseth): get rid of these invalid policies in a
|
||||||
// migration.
|
// migration.
|
||||||
|
// TODO(elle): complete the above TODO in migration from kvdb
|
||||||
|
// to SQL.
|
||||||
oldEdgePolicy, err := deserializeChanEdgePolicy(
|
oldEdgePolicy, err := deserializeChanEdgePolicy(
|
||||||
bytes.NewReader(edgeBytes),
|
bytes.NewReader(edgeBytes),
|
||||||
)
|
)
|
||||||
if err != nil &&
|
if err != nil &&
|
||||||
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) {
|
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) &&
|
||||||
|
!errors.Is(err, ErrParsingExtraTLVBytes) {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -4449,6 +4457,11 @@ func fetchChanEdgePolicy(edges kvdb.RBucket, chanID []byte,
|
|||||||
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
|
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
|
// If the policy contains invalid TLV bytes, we return nil as if
|
||||||
|
// the policy was unknown.
|
||||||
|
case errors.Is(err, ErrParsingExtraTLVBytes):
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -4546,6 +4559,12 @@ func serializeChanEdgePolicy(w io.Writer, edge *models.ChannelEdgePolicy,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate that the ExtraOpaqueData is in fact a valid TLV stream.
|
||||||
|
err = edge.ExtraOpaqueData.ValidateTLV()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %w", ErrParsingExtraTLVBytes, err)
|
||||||
|
}
|
||||||
|
|
||||||
if len(edge.ExtraOpaqueData) > MaxAllowedExtraOpaqueBytes {
|
if len(edge.ExtraOpaqueData) > MaxAllowedExtraOpaqueBytes {
|
||||||
return ErrTooManyExtraOpaqueBytes(len(edge.ExtraOpaqueData))
|
return ErrTooManyExtraOpaqueBytes(len(edge.ExtraOpaqueData))
|
||||||
}
|
}
|
||||||
@@ -4562,15 +4581,18 @@ func serializeChanEdgePolicy(w io.Writer, edge *models.ChannelEdgePolicy,
|
|||||||
|
|
||||||
func deserializeChanEdgePolicy(r io.Reader) (*models.ChannelEdgePolicy, error) {
|
func deserializeChanEdgePolicy(r io.Reader) (*models.ChannelEdgePolicy, error) {
|
||||||
// Deserialize the policy. Note that in case an optional field is not
|
// Deserialize the policy. Note that in case an optional field is not
|
||||||
// found, both an error and a populated policy object are returned.
|
// found or if the edge has invalid TLV data, then both an error and a
|
||||||
edge, deserializeErr := deserializeChanEdgePolicyRaw(r)
|
// populated policy object are returned so that the caller can decide
|
||||||
if deserializeErr != nil &&
|
// if it still wants to use the edge or not.
|
||||||
!errors.Is(deserializeErr, ErrEdgePolicyOptionalFieldNotFound) {
|
edge, err := deserializeChanEdgePolicyRaw(r)
|
||||||
|
if err != nil &&
|
||||||
|
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) &&
|
||||||
|
!errors.Is(err, ErrParsingExtraTLVBytes) {
|
||||||
|
|
||||||
return nil, deserializeErr
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return edge, deserializeErr
|
return edge, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
|
func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
|
||||||
@@ -4657,6 +4679,22 @@ func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
|
|||||||
edge.ExtraOpaqueData = opq[8:]
|
edge.ExtraOpaqueData = opq[8:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempt to extract the inbound fee from the opaque data. If we fail
|
||||||
|
// to parse the TLV here, we return an error we also return the edge
|
||||||
|
// so that the caller can still use it. This is for backwards
|
||||||
|
// compatibility in case we have already persisted some policies that
|
||||||
|
// have invalid TLV data.
|
||||||
|
var inboundFee lnwire.Fee
|
||||||
|
typeMap, err := edge.ExtraOpaqueData.ExtractRecords(&inboundFee)
|
||||||
|
if err != nil {
|
||||||
|
return edge, fmt.Errorf("%w: %w", ErrParsingExtraTLVBytes, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := typeMap[lnwire.FeeRecordType]
|
||||||
|
if ok && val == nil {
|
||||||
|
edge.InboundFee = fn.Some(inboundFee)
|
||||||
|
}
|
||||||
|
|
||||||
return edge, nil
|
return edge, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||||
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,6 +66,15 @@ type ChannelEdgePolicy struct {
|
|||||||
// to. Using this pub key, the channel graph can further be traversed.
|
// to. Using this pub key, the channel graph can further be traversed.
|
||||||
ToNode [33]byte
|
ToNode [33]byte
|
||||||
|
|
||||||
|
// InboundFee is the fee that must be paid for incoming HTLCs.
|
||||||
|
//
|
||||||
|
// NOTE: for our kvdb implementation of the graph store, inbound fees
|
||||||
|
// are still only persisted as part of extra opaque data and so this
|
||||||
|
// field is not explicitly stored but is rather populated from the
|
||||||
|
// ExtraOpaqueData field on deserialization. For our SQL implementation,
|
||||||
|
// this field will be explicitly persisted in the database.
|
||||||
|
InboundFee fn.Option[lnwire.Fee]
|
||||||
|
|
||||||
// ExtraOpaqueData is the set of data that was appended to this
|
// ExtraOpaqueData is the set of data that was appended to this
|
||||||
// message, some of which we may not actually know how to iterate or
|
// message, some of which we may not actually know how to iterate or
|
||||||
// parse. By holding onto this data, we ensure that we're able to
|
// parse. By holding onto this data, we ensure that we're able to
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||||
"github.com/lightningnetwork/lnd/lnutils"
|
"github.com/lightningnetwork/lnd/lnutils"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -360,6 +361,9 @@ type ChannelEdgeUpdate struct {
|
|||||||
// payments.
|
// payments.
|
||||||
Disabled bool
|
Disabled bool
|
||||||
|
|
||||||
|
// InboundFee is the fee that must be paid for incoming HTLCs.
|
||||||
|
InboundFee fn.Option[lnwire.Fee]
|
||||||
|
|
||||||
// ExtraOpaqueData is the set of data that was appended to this message
|
// ExtraOpaqueData is the set of data that was appended to this message
|
||||||
// to fill out the full maximum transport message size. These fields can
|
// to fill out the full maximum transport message size. These fields can
|
||||||
// be used to specify optional data such as custom TLV fields.
|
// be used to specify optional data such as custom TLV fields.
|
||||||
@@ -442,6 +446,7 @@ func (c *ChannelGraph) addToTopologyChange(update *TopologyChange,
|
|||||||
AdvertisingNode: aNode,
|
AdvertisingNode: aNode,
|
||||||
ConnectingNode: cNode,
|
ConnectingNode: cNode,
|
||||||
Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0,
|
Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0,
|
||||||
|
InboundFee: m.InboundFee,
|
||||||
ExtraOpaqueData: m.ExtraOpaqueData,
|
ExtraOpaqueData: m.ExtraOpaqueData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID,
|
|||||||
FeeBaseMSat: lnwire.MilliSatoshi(prand.Int31()),
|
FeeBaseMSat: lnwire.MilliSatoshi(prand.Int31()),
|
||||||
FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int31()),
|
FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int31()),
|
||||||
ToNode: node.PubKeyBytes,
|
ToNode: node.PubKeyBytes,
|
||||||
|
InboundFee: fn.Some(inboundFee),
|
||||||
ExtraOpaqueData: extraOpaqueData,
|
ExtraOpaqueData: extraOpaqueData,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChanUpdateMsgFlags is a bitfield that signals whether optional fields are
|
// ChanUpdateMsgFlags is a bitfield that signals whether optional fields are
|
||||||
@@ -114,6 +115,10 @@ type ChannelUpdate1 struct {
|
|||||||
// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
|
// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
|
||||||
HtlcMaximumMsat MilliSatoshi
|
HtlcMaximumMsat MilliSatoshi
|
||||||
|
|
||||||
|
// InboundFee is an optional TLV record that contains the fee
|
||||||
|
// information for incoming HTLCs.
|
||||||
|
InboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee]
|
||||||
|
|
||||||
// ExtraData is the set of data that was appended to this message to
|
// ExtraData is the set of data that was appended to this message to
|
||||||
// fill out the full maximum transport message size. These fields can
|
// fill out the full maximum transport message size. These fields can
|
||||||
// be used to specify optional data such as custom TLV fields.
|
// be used to specify optional data such as custom TLV fields.
|
||||||
@@ -156,12 +161,27 @@ func (a *ChannelUpdate1) Decode(r io.Reader, _ uint32) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.ExtraOpaqueData.Decode(r)
|
var tlvRecords ExtraOpaqueData
|
||||||
|
if err := ReadElements(r, &tlvRecords); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var inboundFee = a.InboundFee.Zero()
|
||||||
|
typeMap, err := tlvRecords.ExtractRecords(&inboundFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.ExtraOpaqueData.ValidateTLV()
|
val, ok := typeMap[a.InboundFee.TlvType()]
|
||||||
|
if ok && val == nil {
|
||||||
|
a.InboundFee = tlv.SomeRecordT(inboundFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tlvRecords) != 0 {
|
||||||
|
a.ExtraOpaqueData = tlvRecords
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode serializes the target ChannelUpdate into the passed io.Writer
|
// Encode serializes the target ChannelUpdate into the passed io.Writer
|
||||||
@@ -218,6 +238,16 @@ func (a *ChannelUpdate1) Encode(w *bytes.Buffer, pver uint32) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordProducers := make([]tlv.RecordProducer, 0, 1)
|
||||||
|
a.InboundFee.WhenSome(func(fee tlv.RecordT[tlv.TlvType55555, Fee]) {
|
||||||
|
recordProducers = append(recordProducers, &fee)
|
||||||
|
})
|
||||||
|
|
||||||
|
err := EncodeMessageExtraData(&a.ExtraOpaqueData, recordProducers...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, append any extra opaque data.
|
// Finally, append any extra opaque data.
|
||||||
return WriteBytes(w, a.ExtraOpaqueData)
|
return WriteBytes(w, a.ExtraOpaqueData)
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-1
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/fn/v2"
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"pgregory.net/rapid"
|
"pgregory.net/rapid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -406,6 +407,45 @@ func (a *ChannelUpdate1) RandTestMessage(t *rapid.T) Message {
|
|||||||
maxHtlc = 0
|
maxHtlc = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Randomly decide if an inbound fee should be included.
|
||||||
|
// By default, our extra opaque data will just be random TLV but if we
|
||||||
|
// include an inbound fee, then we will also set the record in the
|
||||||
|
// extra opaque data.
|
||||||
|
var (
|
||||||
|
customRecords, _ = RandCustomRecords(t, nil, false)
|
||||||
|
inboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee]
|
||||||
|
)
|
||||||
|
includeInboundFee := rapid.Bool().Draw(t, "includeInboundFee")
|
||||||
|
if includeInboundFee {
|
||||||
|
if customRecords == nil {
|
||||||
|
customRecords = make(CustomRecords)
|
||||||
|
}
|
||||||
|
|
||||||
|
inFeeBase := int32(
|
||||||
|
rapid.IntRange(-1000, 1000).Draw(t, "inFeeBase"),
|
||||||
|
)
|
||||||
|
inFeeProp := int32(
|
||||||
|
rapid.IntRange(-1000, 1000).Draw(t, "inFeeProp"),
|
||||||
|
)
|
||||||
|
fee := Fee{
|
||||||
|
BaseFee: inFeeBase,
|
||||||
|
FeeRate: inFeeProp,
|
||||||
|
}
|
||||||
|
inboundFee = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType55555, Fee](fee),
|
||||||
|
)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
feeRecord := fee.Record()
|
||||||
|
err := feeRecord.Encode(&b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
customRecords[uint64(FeeRecordType)] = b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
extraBytes, err := customRecords.Serialize()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
return &ChannelUpdate1{
|
return &ChannelUpdate1{
|
||||||
Signature: RandSignature(t),
|
Signature: RandSignature(t),
|
||||||
ChainHash: hash,
|
ChainHash: hash,
|
||||||
@@ -428,7 +468,8 @@ func (a *ChannelUpdate1) RandTestMessage(t *rapid.T) Message {
|
|||||||
t, "feeRate"),
|
t, "feeRate"),
|
||||||
),
|
),
|
||||||
HtlcMaximumMsat: maxHtlc,
|
HtlcMaximumMsat: maxHtlc,
|
||||||
ExtraOpaqueData: RandExtraOpaqueData(t, nil),
|
InboundFee: inboundFee,
|
||||||
|
ExtraOpaqueData: extraBytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ func ExtractChannelUpdate(ownerPubKey []byte,
|
|||||||
func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
|
func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
|
||||||
policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate1 {
|
policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate1 {
|
||||||
|
|
||||||
return &lnwire.ChannelUpdate1{
|
update := &lnwire.ChannelUpdate1{
|
||||||
ChainHash: info.ChainHash,
|
ChainHash: info.ChainHash,
|
||||||
ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID),
|
ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID),
|
||||||
Timestamp: uint32(policy.LastUpdate.Unix()),
|
Timestamp: uint32(policy.LastUpdate.Unix()),
|
||||||
@@ -151,6 +152,13 @@ func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
|
|||||||
FeeRate: uint32(policy.FeeProportionalMillionths),
|
FeeRate: uint32(policy.FeeProportionalMillionths),
|
||||||
ExtraOpaqueData: policy.ExtraOpaqueData,
|
ExtraOpaqueData: policy.ExtraOpaqueData,
|
||||||
}
|
}
|
||||||
|
policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
update.InboundFee = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType55555, lnwire.Fee](fee),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
|
// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
|
||||||
|
|||||||
+4
-14
@@ -1205,27 +1205,17 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
|
|||||||
// routing policy into a forwarding policy.
|
// routing policy into a forwarding policy.
|
||||||
var forwardingPolicy *models.ForwardingPolicy
|
var forwardingPolicy *models.ForwardingPolicy
|
||||||
if selfPolicy != nil {
|
if selfPolicy != nil {
|
||||||
var inboundWireFee lnwire.Fee
|
|
||||||
_, err := selfPolicy.ExtraOpaqueData.ExtractRecords(
|
|
||||||
&inboundWireFee,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
inboundFee := models.NewInboundFeeFromWire(
|
|
||||||
inboundWireFee,
|
|
||||||
)
|
|
||||||
|
|
||||||
forwardingPolicy = &models.ForwardingPolicy{
|
forwardingPolicy = &models.ForwardingPolicy{
|
||||||
MinHTLCOut: selfPolicy.MinHTLC,
|
MinHTLCOut: selfPolicy.MinHTLC,
|
||||||
MaxHTLC: selfPolicy.MaxHTLC,
|
MaxHTLC: selfPolicy.MaxHTLC,
|
||||||
BaseFee: selfPolicy.FeeBaseMSat,
|
BaseFee: selfPolicy.FeeBaseMSat,
|
||||||
FeeRate: selfPolicy.FeeProportionalMillionths,
|
FeeRate: selfPolicy.FeeProportionalMillionths,
|
||||||
TimeLockDelta: uint32(selfPolicy.TimeLockDelta),
|
TimeLockDelta: uint32(selfPolicy.TimeLockDelta),
|
||||||
|
|
||||||
InboundFee: inboundFee,
|
|
||||||
}
|
}
|
||||||
|
selfPolicy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
inboundFee := models.NewInboundFeeFromWire(fee)
|
||||||
|
forwardingPolicy.InboundFee = inboundFee
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
p.log.Warnf("Unable to find our forwarding policy "+
|
p.log.Warnf("Unable to find our forwarding policy "+
|
||||||
"for channel %v, using default values",
|
"for channel %v, using default values",
|
||||||
|
|||||||
@@ -127,12 +127,10 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy,
|
|||||||
Edge: edge,
|
Edge: edge,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Extract inbound fees from the ExtraOpaqueData.
|
|
||||||
var inboundWireFee lnwire.Fee
|
var inboundWireFee lnwire.Fee
|
||||||
_, err = edge.ExtraOpaqueData.ExtractRecords(&inboundWireFee)
|
edge.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
if err != nil {
|
inboundWireFee = fee
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
inboundFee := models.NewInboundFeeFromWire(inboundWireFee)
|
inboundFee := models.NewInboundFeeFromWire(inboundWireFee)
|
||||||
|
|
||||||
// Add updated policy to list of policies to send to switch.
|
// Add updated policy to list of policies to send to switch.
|
||||||
@@ -372,6 +370,8 @@ func (r *Manager) updateEdge(chanPoint wire.OutPoint,
|
|||||||
err = fn.MapOptionZ(newSchema.InboundFee,
|
err = fn.MapOptionZ(newSchema.InboundFee,
|
||||||
func(f models.InboundFee) error {
|
func(f models.InboundFee) error {
|
||||||
inboundWireFee := f.ToWire()
|
inboundWireFee := f.ToWire()
|
||||||
|
edge.InboundFee = fn.Some(inboundWireFee)
|
||||||
|
|
||||||
return edge.ExtraOpaqueData.PackRecords(
|
return edge.ExtraOpaqueData.PackRecords(
|
||||||
&inboundWireFee,
|
&inboundWireFee,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -666,15 +666,25 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
getExtraData := func(
|
getInboundFees := func(
|
||||||
end *testChannelEnd) lnwire.ExtraOpaqueData {
|
end *testChannelEnd) fn.Option[lnwire.Fee] {
|
||||||
|
|
||||||
var extraData lnwire.ExtraOpaqueData
|
|
||||||
inboundFee := lnwire.Fee{
|
inboundFee := lnwire.Fee{
|
||||||
BaseFee: int32(end.InboundFeeBaseMsat),
|
BaseFee: int32(end.InboundFeeBaseMsat),
|
||||||
FeeRate: int32(end.InboundFeeRate),
|
FeeRate: int32(end.InboundFeeRate),
|
||||||
}
|
}
|
||||||
require.NoError(t, extraData.PackRecords(&inboundFee))
|
|
||||||
|
return fn.Some(inboundFee)
|
||||||
|
}
|
||||||
|
getExtraData := func(
|
||||||
|
end *testChannelEnd) lnwire.ExtraOpaqueData {
|
||||||
|
|
||||||
|
var extraData lnwire.ExtraOpaqueData
|
||||||
|
|
||||||
|
inboundFee := getInboundFees(end)
|
||||||
|
inboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||||
|
require.NoError(t, extraData.PackRecords(&fee))
|
||||||
|
})
|
||||||
|
|
||||||
return extraData
|
return extraData
|
||||||
}
|
}
|
||||||
@@ -701,6 +711,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
|||||||
FeeBaseMSat: node1.FeeBaseMsat,
|
FeeBaseMSat: node1.FeeBaseMsat,
|
||||||
FeeProportionalMillionths: node1.FeeRate,
|
FeeProportionalMillionths: node1.FeeRate,
|
||||||
ToNode: node2Vertex,
|
ToNode: node2Vertex,
|
||||||
|
InboundFee: getInboundFees(node1), //nolint:ll
|
||||||
ExtraOpaqueData: getExtraData(node1),
|
ExtraOpaqueData: getExtraData(node1),
|
||||||
}
|
}
|
||||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||||
@@ -731,6 +742,7 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
|||||||
FeeBaseMSat: node2.FeeBaseMsat,
|
FeeBaseMSat: node2.FeeBaseMsat,
|
||||||
FeeProportionalMillionths: node2.FeeRate,
|
FeeProportionalMillionths: node2.FeeRate,
|
||||||
ToNode: node1Vertex,
|
ToNode: node1Vertex,
|
||||||
|
InboundFee: getInboundFees(node2), //nolint:ll
|
||||||
ExtraOpaqueData: getExtraData(node2),
|
ExtraOpaqueData: getExtraData(node2),
|
||||||
}
|
}
|
||||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||||
|
|||||||
+4
-28
@@ -6776,23 +6776,6 @@ func marshalExtraOpaqueData(data []byte) map[uint64][]byte {
|
|||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractInboundFeeSafe tries to extract the inbound fee from the given extra
|
|
||||||
// opaque data tlv block. If parsing fails, a zero inbound fee is returned. This
|
|
||||||
// function is typically used on unvalidated data coming stored in the database.
|
|
||||||
// There is not much we can do other than ignoring errors here.
|
|
||||||
func extractInboundFeeSafe(data lnwire.ExtraOpaqueData) lnwire.Fee {
|
|
||||||
var inboundFee lnwire.Fee
|
|
||||||
|
|
||||||
_, err := data.ExtractRecords(&inboundFee)
|
|
||||||
if err != nil {
|
|
||||||
// Return zero fee. Do not return the inboundFee variable
|
|
||||||
// because it may be undefined.
|
|
||||||
return lnwire.Fee{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inboundFee
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
|
func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
|
||||||
c1, c2 *models.ChannelEdgePolicy) *lnrpc.ChannelEdge {
|
c1, c2 *models.ChannelEdgePolicy) *lnrpc.ChannelEdge {
|
||||||
|
|
||||||
@@ -6842,7 +6825,7 @@ func marshalDBRoutingPolicy(
|
|||||||
disabled := policy.ChannelFlags&lnwire.ChanUpdateDisabled != 0
|
disabled := policy.ChannelFlags&lnwire.ChanUpdateDisabled != 0
|
||||||
|
|
||||||
customRecords := marshalExtraOpaqueData(policy.ExtraOpaqueData)
|
customRecords := marshalExtraOpaqueData(policy.ExtraOpaqueData)
|
||||||
inboundFee := extractInboundFeeSafe(policy.ExtraOpaqueData)
|
inboundFee := policy.InboundFee.UnwrapOr(lnwire.Fee{})
|
||||||
|
|
||||||
return &lnrpc.RoutingPolicy{
|
return &lnrpc.RoutingPolicy{
|
||||||
TimeLockDelta: uint32(policy.TimeLockDelta),
|
TimeLockDelta: uint32(policy.TimeLockDelta),
|
||||||
@@ -7349,9 +7332,7 @@ func marshallTopologyChange(
|
|||||||
customRecords := marshalExtraOpaqueData(
|
customRecords := marshalExtraOpaqueData(
|
||||||
channelUpdate.ExtraOpaqueData,
|
channelUpdate.ExtraOpaqueData,
|
||||||
)
|
)
|
||||||
inboundFee := extractInboundFeeSafe(
|
inboundFee := channelUpdate.InboundFee.UnwrapOr(lnwire.Fee{})
|
||||||
channelUpdate.ExtraOpaqueData,
|
|
||||||
)
|
|
||||||
|
|
||||||
channelUpdates[i] = &lnrpc.ChannelEdgeUpdate{
|
channelUpdates[i] = &lnrpc.ChannelEdgeUpdate{
|
||||||
ChanId: channelUpdate.ChanID,
|
ChanId: channelUpdate.ChanID,
|
||||||
@@ -7698,14 +7679,9 @@ func (r *rpcServer) FeeReport(ctx context.Context,
|
|||||||
edgePolicy.FeeProportionalMillionths
|
edgePolicy.FeeProportionalMillionths
|
||||||
feeRate := float64(feeRateFixedPoint) / feeBase
|
feeRate := float64(feeRateFixedPoint) / feeBase
|
||||||
|
|
||||||
// Decode inbound fee from extra data.
|
inboundFee := edgePolicy.InboundFee.UnwrapOr(
|
||||||
var inboundFee lnwire.Fee
|
lnwire.Fee{},
|
||||||
_, err := edgePolicy.ExtraOpaqueData.ExtractRecords(
|
|
||||||
&inboundFee,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(roasbeef): also add stats for revenue for each
|
// TODO(roasbeef): also add stats for revenue for each
|
||||||
// channel
|
// channel
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ const (
|
|||||||
// second defined unsigned TLV range used in pure TLV messages.
|
// second defined unsigned TLV range used in pure TLV messages.
|
||||||
pureTLVSecondUnsignedTypeRangeStart uint32 = 3000000000
|
pureTLVSecondUnsignedTypeRangeStart uint32 = 3000000000
|
||||||
|
|
||||||
|
// inboundFeeType defines the TLV type used within the channel_update
|
||||||
|
// message to indicate the inbound fee for a channel.
|
||||||
|
inboundFeeType = 55555
|
||||||
|
|
||||||
defaultOutputFile = "tlv_types_generated.go"
|
defaultOutputFile = "tlv_types_generated.go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +33,7 @@ const (
|
|||||||
var typeMarkers = map[uint32]struct{}{
|
var typeMarkers = map[uint32]struct{}{
|
||||||
pureTLVSecondSignedTypeRangeStart: {},
|
pureTLVSecondSignedTypeRangeStart: {},
|
||||||
pureTLVSecondUnsignedTypeRangeStart: {},
|
pureTLVSecondUnsignedTypeRangeStart: {},
|
||||||
|
inboundFeeType: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeCodeTemplate = `// Code generated by tlv/internal/gen; DO NOT EDIT.
|
const typeCodeTemplate = `// Code generated by tlv/internal/gen; DO NOT EDIT.
|
||||||
|
|||||||
@@ -3012,6 +3012,16 @@ func (t *tlvType300) tlv() {}
|
|||||||
|
|
||||||
type TlvType300 = *tlvType300
|
type TlvType300 = *tlvType300
|
||||||
|
|
||||||
|
type tlvType55555 struct{}
|
||||||
|
|
||||||
|
func (t *tlvType55555) TypeVal() Type {
|
||||||
|
return 55555
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tlvType55555) tlv() {}
|
||||||
|
|
||||||
|
type TlvType55555 = *tlvType55555
|
||||||
|
|
||||||
type tlvType65536 struct{}
|
type tlvType65536 struct{}
|
||||||
|
|
||||||
func (t *tlvType65536) TypeVal() Type {
|
func (t *tlvType65536) TypeVal() Type {
|
||||||
|
|||||||
Reference in new issue
Block a user