mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-24 22:58:18 +01:00
Find and replace all nolint instances refering to the `lll` linter and replace with `ll` which is the name of our custom version of the `lll` linter which can be used to ignore log lines during linting. The next commit will do the configuration of the custom linter and disable the default one.
441 lines
14 KiB
Go
441 lines
14 KiB
Go
package lnwire
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/lightningnetwork/lnd/tlv"
|
|
)
|
|
|
|
const (
|
|
defaultCltvExpiryDelta = uint16(80)
|
|
defaultHtlcMinMsat = MilliSatoshi(1)
|
|
defaultFeeBaseMsat = uint32(1000)
|
|
defaultFeeProportionalMillionths = uint32(1)
|
|
)
|
|
|
|
// ChannelUpdate2 message is used after taproot channel has been initially
|
|
// announced. Each side independently announces its fees and minimum expiry for
|
|
// HTLCs and other parameters. This message is also used to redeclare initially
|
|
// set channel parameters.
|
|
type ChannelUpdate2 struct {
|
|
// Signature is used to validate the announced data and prove the
|
|
// ownership of node id.
|
|
Signature Sig
|
|
|
|
// ChainHash denotes the target chain that this channel was opened
|
|
// within. This value should be the genesis hash of the target chain.
|
|
// Along with the short channel ID, this uniquely identifies the
|
|
// channel globally in a blockchain.
|
|
ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
|
|
|
|
// ShortChannelID is the unique description of the funding transaction.
|
|
ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID]
|
|
|
|
// BlockHeight allows ordering in the case of multiple announcements. We
|
|
// should ignore the message if block height is not greater than the
|
|
// last-received. The block height must always be greater or equal to
|
|
// the block height that the channel funding transaction was confirmed
|
|
// in.
|
|
BlockHeight tlv.RecordT[tlv.TlvType4, uint32]
|
|
|
|
// DisabledFlags is an optional bitfield that describes various reasons
|
|
// that the node is communicating that the channel should be considered
|
|
// disabled.
|
|
DisabledFlags tlv.RecordT[tlv.TlvType6, ChanUpdateDisableFlags]
|
|
|
|
// SecondPeer is used to indicate which node the channel node has
|
|
// created and signed this message. If this field is present, it was
|
|
// node 2 otherwise it was node 1.
|
|
SecondPeer tlv.OptionalRecordT[tlv.TlvType8, TrueBoolean]
|
|
|
|
// CLTVExpiryDelta is the minimum number of blocks this node requires to
|
|
// be added to the expiry of HTLCs. This is a security parameter
|
|
// determined by the node operator. This value represents the required
|
|
// gap between the time locks of the incoming and outgoing HTLC's set
|
|
// to this node.
|
|
CLTVExpiryDelta tlv.RecordT[tlv.TlvType10, uint16]
|
|
|
|
// HTLCMinimumMsat is the minimum HTLC value which will be accepted.
|
|
HTLCMinimumMsat tlv.RecordT[tlv.TlvType12, MilliSatoshi]
|
|
|
|
// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
|
|
HTLCMaximumMsat tlv.RecordT[tlv.TlvType14, MilliSatoshi]
|
|
|
|
// FeeBaseMsat is the base fee that must be used for incoming HTLC's to
|
|
// this particular channel. This value will be tacked onto the required
|
|
// for a payment independent of the size of the payment.
|
|
FeeBaseMsat tlv.RecordT[tlv.TlvType16, uint32]
|
|
|
|
// FeeProportionalMillionths is the fee rate that will be charged per
|
|
// millionth of a satoshi.
|
|
FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
|
|
|
|
// ExtraOpaqueData is the set of data that was appended to this message
|
|
// to fill out the full maximum transport message size. These fields can
|
|
// be used to specify optional data such as custom TLV fields.
|
|
ExtraOpaqueData ExtraOpaqueData
|
|
}
|
|
|
|
// Decode deserializes a serialized ChannelUpdate2 stored in the passed
|
|
// io.Reader observing the specified protocol version.
|
|
//
|
|
// This is part of the lnwire.Message interface.
|
|
func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
|
|
err := ReadElement(r, &c.Signature)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Signature.ForceSchnorr()
|
|
|
|
return c.DecodeTLVRecords(r)
|
|
}
|
|
|
|
// DecodeTLVRecords decodes only the TLV section of the message.
|
|
func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
|
|
// First extract into extra opaque data.
|
|
var tlvRecords ExtraOpaqueData
|
|
if err := ReadElements(r, &tlvRecords); err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
|
secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]()
|
|
)
|
|
typeMap, err := tlvRecords.ExtractRecords(
|
|
&chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags,
|
|
&secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
|
|
&c.HTLCMaximumMsat, &c.FeeBaseMsat,
|
|
&c.FeeProportionalMillionths,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
|
|
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
|
|
if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
|
|
c.ChainHash.Val = chainHash.Val
|
|
}
|
|
|
|
// The presence of the second_peer tlv type indicates "true".
|
|
if _, ok := typeMap[c.SecondPeer.TlvType()]; ok {
|
|
c.SecondPeer = tlv.SomeRecordT(secondPeer)
|
|
}
|
|
|
|
// If the CLTV expiry delta was not encoded, then set it to the default
|
|
// value.
|
|
if _, ok := typeMap[c.CLTVExpiryDelta.TlvType()]; !ok {
|
|
c.CLTVExpiryDelta.Val = defaultCltvExpiryDelta
|
|
}
|
|
|
|
// If the HTLC Minimum msat was not encoded, then set it to the default
|
|
// value.
|
|
if _, ok := typeMap[c.HTLCMinimumMsat.TlvType()]; !ok {
|
|
c.HTLCMinimumMsat.Val = defaultHtlcMinMsat
|
|
}
|
|
|
|
// If the base fee was not encoded, then set it to the default value.
|
|
if _, ok := typeMap[c.FeeBaseMsat.TlvType()]; !ok {
|
|
c.FeeBaseMsat.Val = defaultFeeBaseMsat
|
|
}
|
|
|
|
// If the proportional fee was not encoded, then set it to the default
|
|
// value.
|
|
if _, ok := typeMap[c.FeeProportionalMillionths.TlvType()]; !ok {
|
|
c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:ll
|
|
}
|
|
|
|
if len(tlvRecords) != 0 {
|
|
c.ExtraOpaqueData = tlvRecords
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Encode serializes the target ChannelUpdate2 into the passed io.Writer
|
|
// observing the protocol version specified.
|
|
//
|
|
// This is part of the lnwire.Message interface.
|
|
func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
|
|
_, err := w.Write(c.Signature.RawBytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = c.DataToSign()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return WriteBytes(w, c.ExtraOpaqueData)
|
|
}
|
|
|
|
// DataToSign is used to retrieve part of the announcement message which should
|
|
// be signed. For the ChannelUpdate2 message, this includes the serialised TLV
|
|
// records.
|
|
func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
|
|
// The chain-hash record is only included if it is _not_ equal to the
|
|
// bitcoin mainnet genisis block hash.
|
|
var recordProducers []tlv.RecordProducer
|
|
if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
|
|
hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
|
hash.Val = c.ChainHash.Val
|
|
|
|
recordProducers = append(recordProducers, &hash)
|
|
}
|
|
|
|
recordProducers = append(recordProducers,
|
|
&c.ShortChannelID, &c.BlockHeight,
|
|
)
|
|
|
|
// Only include the disable flags if any bit is set.
|
|
if !c.DisabledFlags.Val.IsEnabled() {
|
|
recordProducers = append(recordProducers, &c.DisabledFlags)
|
|
}
|
|
|
|
// We only need to encode the second peer boolean if it is true
|
|
c.SecondPeer.WhenSome(func(r tlv.RecordT[tlv.TlvType8, TrueBoolean]) {
|
|
recordProducers = append(recordProducers, &r)
|
|
})
|
|
|
|
// We only encode the cltv expiry delta if it is not equal to the
|
|
// default.
|
|
if c.CLTVExpiryDelta.Val != defaultCltvExpiryDelta {
|
|
recordProducers = append(recordProducers, &c.CLTVExpiryDelta)
|
|
}
|
|
|
|
if c.HTLCMinimumMsat.Val != defaultHtlcMinMsat {
|
|
recordProducers = append(recordProducers, &c.HTLCMinimumMsat)
|
|
}
|
|
|
|
recordProducers = append(recordProducers, &c.HTLCMaximumMsat)
|
|
|
|
if c.FeeBaseMsat.Val != defaultFeeBaseMsat {
|
|
recordProducers = append(recordProducers, &c.FeeBaseMsat)
|
|
}
|
|
|
|
if c.FeeProportionalMillionths.Val != defaultFeeProportionalMillionths {
|
|
recordProducers = append(
|
|
recordProducers, &c.FeeProportionalMillionths,
|
|
)
|
|
}
|
|
|
|
err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.ExtraOpaqueData, nil
|
|
}
|
|
|
|
// MsgType returns the integer uniquely identifying this message type on the
|
|
// wire.
|
|
//
|
|
// This is part of the lnwire.Message interface.
|
|
func (c *ChannelUpdate2) MsgType() MessageType {
|
|
return MsgChannelUpdate2
|
|
}
|
|
|
|
func (c *ChannelUpdate2) ExtraData() ExtraOpaqueData {
|
|
return c.ExtraOpaqueData
|
|
}
|
|
|
|
// A compile time check to ensure ChannelUpdate2 implements the
|
|
// lnwire.Message interface.
|
|
var _ Message = (*ChannelUpdate2)(nil)
|
|
|
|
// SCID returns the ShortChannelID of the channel that the update applies to.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) SCID() ShortChannelID {
|
|
return c.ShortChannelID.Val
|
|
}
|
|
|
|
// IsNode1 is true if the update was produced by node 1 of the channel peers.
|
|
// Node 1 is the node with the lexicographically smaller public key.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) IsNode1() bool {
|
|
return c.SecondPeer.IsNone()
|
|
}
|
|
|
|
// IsDisabled is true if the update is announcing that the channel should be
|
|
// considered disabled.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) IsDisabled() bool {
|
|
return !c.DisabledFlags.Val.IsEnabled()
|
|
}
|
|
|
|
// GetChainHash returns the hash of the chain that the message is referring to.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) GetChainHash() chainhash.Hash {
|
|
return c.ChainHash.Val
|
|
}
|
|
|
|
// ForwardingPolicy returns the set of forwarding constraints of the update.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) ForwardingPolicy() *ForwardingPolicy {
|
|
return &ForwardingPolicy{
|
|
TimeLockDelta: c.CLTVExpiryDelta.Val,
|
|
BaseFee: MilliSatoshi(c.FeeBaseMsat.Val),
|
|
FeeRate: MilliSatoshi(c.FeeProportionalMillionths.Val),
|
|
MinHTLC: c.HTLCMinimumMsat.Val,
|
|
HasMaxHTLC: true,
|
|
MaxHTLC: c.HTLCMaximumMsat.Val,
|
|
}
|
|
}
|
|
|
|
// CmpAge can be used to determine if the update is older or newer than the
|
|
// passed update. It returns 1 if this update is newer, -1 if it is older, and
|
|
// 0 if they are the same age.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) CmpAge(update ChannelUpdate) (CompareResult, error) {
|
|
other, ok := update.(*ChannelUpdate2)
|
|
if !ok {
|
|
return 0, fmt.Errorf("expected *ChannelUpdate2, got: %T",
|
|
update)
|
|
}
|
|
|
|
switch {
|
|
case c.BlockHeight.Val > other.BlockHeight.Val:
|
|
return GreaterThan, nil
|
|
case c.BlockHeight.Val < other.BlockHeight.Val:
|
|
return LessThan, nil
|
|
default:
|
|
return EqualTo, nil
|
|
}
|
|
}
|
|
|
|
// SetDisabledFlag can be used to adjust the disabled flag of an update.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) SetDisabledFlag(disabled bool) {
|
|
if disabled {
|
|
c.DisabledFlags.Val |= ChanUpdateDisableIncoming
|
|
c.DisabledFlags.Val |= ChanUpdateDisableOutgoing
|
|
} else {
|
|
c.DisabledFlags.Val &^= ChanUpdateDisableIncoming
|
|
c.DisabledFlags.Val &^= ChanUpdateDisableOutgoing
|
|
}
|
|
}
|
|
|
|
// SetSCID can be used to overwrite the SCID of the update.
|
|
//
|
|
// NOTE: this is part of the ChannelUpdate interface.
|
|
func (c *ChannelUpdate2) SetSCID(scid ShortChannelID) {
|
|
c.ShortChannelID.Val = scid
|
|
}
|
|
|
|
// A compile time check to ensure ChannelUpdate2 implements the
|
|
// lnwire.ChannelUpdate interface.
|
|
var _ ChannelUpdate = (*ChannelUpdate2)(nil)
|
|
|
|
// ChanUpdateDisableFlags is a bit vector that can be used to indicate various
|
|
// reasons for the channel being marked as disabled.
|
|
type ChanUpdateDisableFlags uint8
|
|
|
|
const (
|
|
// ChanUpdateDisableIncoming is a bit indicates that a channel is
|
|
// disabled in the inbound direction meaning that the node broadcasting
|
|
// the update is communicating that they cannot receive funds.
|
|
ChanUpdateDisableIncoming ChanUpdateDisableFlags = 1 << iota
|
|
|
|
// ChanUpdateDisableOutgoing is a bit indicates that a channel is
|
|
// disabled in the outbound direction meaning that the node broadcasting
|
|
// the update is communicating that they cannot send or route funds.
|
|
ChanUpdateDisableOutgoing = 2
|
|
)
|
|
|
|
// IncomingDisabled returns true if the ChanUpdateDisableIncoming bit is set.
|
|
func (c ChanUpdateDisableFlags) IncomingDisabled() bool {
|
|
return c&ChanUpdateDisableIncoming == ChanUpdateDisableIncoming
|
|
}
|
|
|
|
// OutgoingDisabled returns true if the ChanUpdateDisableOutgoing bit is set.
|
|
func (c ChanUpdateDisableFlags) OutgoingDisabled() bool {
|
|
return c&ChanUpdateDisableOutgoing == ChanUpdateDisableOutgoing
|
|
}
|
|
|
|
// IsEnabled returns true if none of the disable bits are set.
|
|
func (c ChanUpdateDisableFlags) IsEnabled() bool {
|
|
return c == 0
|
|
}
|
|
|
|
// String returns the bitfield flags as a string.
|
|
func (c ChanUpdateDisableFlags) String() string {
|
|
return fmt.Sprintf("%08b", c)
|
|
}
|
|
|
|
// Record returns the tlv record for the disable flags.
|
|
func (c *ChanUpdateDisableFlags) Record() tlv.Record {
|
|
return tlv.MakeStaticRecord(0, c, 1, encodeDisableFlags,
|
|
decodeDisableFlags)
|
|
}
|
|
|
|
func encodeDisableFlags(w io.Writer, val interface{}, buf *[8]byte) error {
|
|
if v, ok := val.(*ChanUpdateDisableFlags); ok {
|
|
flagsInt := uint8(*v)
|
|
|
|
return tlv.EUint8(w, &flagsInt, buf)
|
|
}
|
|
|
|
return tlv.NewTypeForEncodingErr(val, "lnwire.ChanUpdateDisableFlags")
|
|
}
|
|
|
|
func decodeDisableFlags(r io.Reader, val interface{}, buf *[8]byte,
|
|
l uint64) error {
|
|
|
|
if v, ok := val.(*ChanUpdateDisableFlags); ok {
|
|
var flagsInt uint8
|
|
err := tlv.DUint8(r, &flagsInt, buf, l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*v = ChanUpdateDisableFlags(flagsInt)
|
|
|
|
return nil
|
|
}
|
|
|
|
return tlv.NewTypeForDecodingErr(val, "lnwire.ChanUpdateDisableFlags",
|
|
l, l)
|
|
}
|
|
|
|
// TrueBoolean is a record that indicates true or false using the presence of
|
|
// the record. If the record is absent, it indicates false. If it is present,
|
|
// it indicates true.
|
|
type TrueBoolean struct{}
|
|
|
|
// Record returns the tlv record for the boolean entry.
|
|
func (b *TrueBoolean) Record() tlv.Record {
|
|
return tlv.MakeStaticRecord(
|
|
0, b, 0, booleanEncoder, booleanDecoder,
|
|
)
|
|
}
|
|
|
|
func booleanEncoder(_ io.Writer, val interface{}, _ *[8]byte) error {
|
|
if _, ok := val.(*TrueBoolean); ok {
|
|
return nil
|
|
}
|
|
|
|
return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
|
|
}
|
|
|
|
func booleanDecoder(_ io.Reader, val interface{}, _ *[8]byte,
|
|
l uint64) error {
|
|
|
|
if _, ok := val.(*TrueBoolean); ok && (l == 0 || l == 1) {
|
|
return nil
|
|
}
|
|
|
|
return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
|
|
}
|