Files
lnd/graph/db/models/channel_edge_policy.go
Elle Mouton a73c45946b multi: add ChanEdgePolicyFromWire constructor for ChannelEdgePolicy
Add ChanEdgePolicyFromWire to construct ChannelEdgePolicy from channel
update messages, centralizing v1/v2 field mapping.
Update call sites to use the helper:

- discovery/gossiper: handleChanUpdate
- graph/builder: ApplyChannelUpdate
- routing/router_test: ApplyChannelUpdate test helper

This consolidates update-to-policy conversion logic across versions.
2026-02-13 07:21:24 +02:00

194 lines
6.9 KiB
Go

package models
import (
"fmt"
"time"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/lnwire"
)
// ChannelEdgePolicy represents a *directed* edge within the channel graph. For
// each channel in the database, there are two distinct edges: one for each
// possible direction of travel along the channel. The edges themselves hold
// information concerning fees, and minimum time-lock information which is
// utilized during path finding.
type ChannelEdgePolicy struct {
// Version is the gossip version of the channel update that produced
// this policy.
Version lnwire.GossipVersion
// SigBytes is the raw bytes of the signature of the channel edge
// policy. We'll only parse these if the caller needs to access the
// signature for validation purposes.
SigBytes []byte
// ChannelID is the unique channel ID for the channel. The first 3
// bytes are the block height, the next 3 the index within the block,
// and the last 2 bytes are the output index for the channel.
ChannelID uint64
// LastUpdate is the last time an authenticated edge for this channel
// was received.
LastUpdate time.Time
// LastBlockHeight is the block height that timestamps the last update
// for v2 channel updates.
LastBlockHeight uint32
// SecondPeer indicates whether this policy was announced by the second
// peer in the channel for v2 channel updates.
SecondPeer bool
// MessageFlags is a bitfield which indicates the presence of optional
// fields (like max_htlc) in the policy.
MessageFlags lnwire.ChanUpdateMsgFlags
// ChannelFlags is a bitfield which signals the capabilities of the
// channel as well as the directed edge this update applies to.
ChannelFlags lnwire.ChanUpdateChanFlags
// DisableFlags is a v2-specific bitfield which signals whether the
// channel is disabled for incoming or outgoing traffic.
DisableFlags lnwire.ChanUpdateDisableFlags
// TimeLockDelta is the number of blocks this node will subtract from
// the expiry of an incoming HTLC. This value expresses the time buffer
// the node would like to HTLC exchanges.
TimeLockDelta uint16
// MinHTLC is the smallest value HTLC this node will forward, expressed
// in millisatoshi.
MinHTLC lnwire.MilliSatoshi
// MaxHTLC is the largest value HTLC this node will forward, expressed
// in millisatoshi.
MaxHTLC lnwire.MilliSatoshi
// FeeBaseMSat is the base HTLC fee that will be charged for forwarding
// ANY HTLC, expressed in mSAT's.
FeeBaseMSat lnwire.MilliSatoshi
// FeeProportionalMillionths is the rate that the node will charge for
// HTLCs for each millionth of a satoshi forwarded.
FeeProportionalMillionths lnwire.MilliSatoshi
// ToNode is the public key of the node that this directed edge leads
// to. Using this pub key, the channel graph can further be traversed.
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
// 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
// properly validate the set of signatures that cover these new fields,
// and ensure we're able to make upgrades to the network in a forwards
// compatible manner.
ExtraOpaqueData lnwire.ExtraOpaqueData
// ExtraSignedFields are the extra signed fields found in v2 channel
// updates.
ExtraSignedFields map[uint64][]byte
}
// ChanEdgePolicyFromWire constructs a ChannelEdgePolicy from a channel update
// message.
func ChanEdgePolicyFromWire(scid uint64,
update lnwire.ChannelUpdate) (*ChannelEdgePolicy, error) {
switch upd := update.(type) {
case *lnwire.ChannelUpdate1:
//nolint:ll
return &ChannelEdgePolicy{
Version: lnwire.GossipVersion1,
SigBytes: upd.Signature.ToSignatureBytes(),
ChannelID: scid,
LastUpdate: time.Unix(int64(upd.Timestamp), 0),
MessageFlags: upd.MessageFlags,
ChannelFlags: upd.ChannelFlags,
TimeLockDelta: upd.TimeLockDelta,
MinHTLC: upd.HtlcMinimumMsat,
MaxHTLC: upd.HtlcMaximumMsat,
FeeBaseMSat: lnwire.MilliSatoshi(upd.BaseFee),
FeeProportionalMillionths: lnwire.MilliSatoshi(upd.FeeRate),
InboundFee: upd.InboundFee.ValOpt(),
ExtraOpaqueData: upd.ExtraOpaqueData,
}, nil
case *lnwire.ChannelUpdate2:
return &ChannelEdgePolicy{
Version: lnwire.GossipVersion2,
SigBytes: upd.Signature.Val.ToSignatureBytes(),
ChannelID: scid,
LastBlockHeight: upd.BlockHeight.Val,
SecondPeer: upd.SecondPeer.IsSome(),
DisableFlags: upd.DisabledFlags.Val,
TimeLockDelta: upd.CLTVExpiryDelta.Val,
MinHTLC: upd.HTLCMinimumMsat.Val,
MaxHTLC: upd.HTLCMaximumMsat.Val,
FeeBaseMSat: lnwire.MilliSatoshi(
upd.FeeBaseMsat.Val,
),
FeeProportionalMillionths: lnwire.MilliSatoshi(
upd.FeeProportionalMillionths.Val,
),
InboundFee: upd.InboundFee.ValOpt(),
ExtraSignedFields: upd.ExtraSignedFields,
}, nil
}
return nil, fmt.Errorf("unknown channel update version: %v",
update.MsgType())
}
// IsNode1 returns true if this policy was announced by the channel's node_1.
func (c *ChannelEdgePolicy) IsNode1() bool {
if c.Version == lnwire.GossipVersion1 {
return c.ChannelFlags&lnwire.ChanUpdateDirection == 0
}
return !c.SecondPeer
}
// IsDisabled determines whether the edge has the disabled bit set.
//
// NOTE: for v2 channel updates, we return true here only if both the incoming
// and outgoing disabled bits are set.
func (c *ChannelEdgePolicy) IsDisabled() bool {
if c.Version == lnwire.GossipVersion1 {
return c.ChannelFlags.IsDisabled()
}
return !c.DisableFlags.IsEnabled()
}
// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
// the passed active payment channel. This value is currently computed as
// specified in BOLT07, but will likely change in the near future.
func (c *ChannelEdgePolicy) ComputeFee(
amt lnwire.MilliSatoshi) lnwire.MilliSatoshi {
return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
}
// String returns a human-readable version of the channel edge policy.
func (c *ChannelEdgePolicy) String() string {
if c.Version == lnwire.GossipVersion1 {
return fmt.Sprintf("ChannelID=%v, MessageFlags=%v, "+
"ChannelFlags=%v, LastUpdate=%v", c.ChannelID,
c.MessageFlags, c.ChannelFlags, c.LastUpdate)
}
return fmt.Sprintf("ChannelID=%v, Node1=%v, DisableFlags=%v, "+
"BlockHeight=%v", c.ChannelID, !c.SecondPeer,
c.DisableFlags, c.LastBlockHeight)
}