Skip to content
Merged
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
5 changes: 5 additions & 0 deletions config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ type AuxComponents struct {
// AuxContractResolver is an optional interface that can be used to
// modify the way contracts are resolved.
AuxContractResolver fn.Option[lnwallet.AuxContractResolver]

// AuxChannelNegotiator is an optional interface that allows aux channel
// implementations to inject and process custom records over channel
// related wire messages.
AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator]
}

// DefaultWalletImpl is the default implementation of our normal, btcwallet
Expand Down
5 changes: 5 additions & 0 deletions docs/release-notes/release-notes-0.20.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ a certain amount of msats.
[allow](https://github.com/lightningnetwork/lnd/pull/10087)
`conf_target=1`. Previously they required `conf_target >= 2`.

* A new AuxComponent was added named AuxChannelNegotiator. This component aids
with custom data communication for aux channels, by injecting and handling
data in channel related wire messages. See
[PR](https://github.com/lightningnetwork/lnd/pull/10182) for more info.

## RPC Additions
* When querying [`ForwardingEvents`](https://github.com/lightningnetwork/lnd/pull/9813)
logs, the response now include the incoming and outgoing htlc indices of the payment
Expand Down
13 changes: 13 additions & 0 deletions funding/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,11 @@ type Config struct {
// AuxResolver is an optional interface that can be used to modify the
// way contracts are resolved.
AuxResolver fn.Option[lnwallet.AuxContractResolver]

// AuxChannelNegotiator is an optional interface that allows aux channel
// implementations to inject and process custom records over channel
// related wire messages.
AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator]
}

// Manager acts as an orchestrator/bridge between the wallet's
Expand Down Expand Up @@ -4019,6 +4024,14 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen

defer f.wg.Done()

// Notify the aux hook that the specified peer just established a
// channel with us, identified by the given channel ID.
f.cfg.AuxChannelNegotiator.WhenSome(
func(acn lnwallet.AuxChannelNegotiator) {
acn.ProcessChannelReady(msg.ChanID, peer.PubKey())
},
)

// If we are in development mode, we'll wait for specified duration
// before processing the channel ready message.
if f.cfg.Dev != nil {
Expand Down
21 changes: 21 additions & 0 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ type ChannelLinkConfig struct {
// used to manage the bandwidth of the link.
AuxTrafficShaper fn.Option[AuxTrafficShaper]

// AuxChannelNegotiator is an optional interface that allows aux channel
// implementations to inject and process custom records over channel
// related wire messages.
AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator]

// QuiescenceTimeout is the max duration that the channel can be
// quiesced. Any dependent protocols (dynamic commitments, splicing,
// etc.) must finish their operations under this timeout value,
Expand Down Expand Up @@ -987,6 +992,22 @@ func (l *channelLink) syncChanStates(ctx context.Context) error {
// In any case, we'll then process their ChanSync message.
l.log.Info("received re-establishment message from remote side")

// If we have an AuxChannelNegotiator we notify any external
// component for this message. This serves as a notification
// that the reestablish message was received.
l.cfg.AuxChannelNegotiator.WhenSome(
func(acn lnwallet.AuxChannelNegotiator) {
fundingPoint := l.channel.ChannelPoint()
cid := lnwire.NewChanIDFromOutPoint(
fundingPoint,
)

acn.ProcessReestablish(
cid, l.cfg.Peer.PubKey(),
)
},
)

var (
openedCircuits []CircuitKey
closedCircuits []CircuitKey
Expand Down
8 changes: 8 additions & 0 deletions lnwallet/aux_leaf_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/tlv"
)

Expand Down Expand Up @@ -97,6 +98,10 @@ type AuxChanState struct {
// funding output.
TapscriptRoot fn.Option[chainhash.Hash]

// PeerPubKey is the peer pub key of the peer we've established this
// channel with.
PeerPubKey route.Vertex

// CustomBlob is an optional blob that can be used to store information
// specific to a custom channel type. This information is only created
// at channel funding time, and after wards is to be considered
Expand All @@ -106,6 +111,8 @@ type AuxChanState struct {

// NewAuxChanState creates a new AuxChanState from the given channel state.
func NewAuxChanState(chanState *channeldb.OpenChannel) AuxChanState {
peerPub := chanState.IdentityPub.SerializeCompressed()

return AuxChanState{
ChanType: chanState.ChanType,
FundingOutpoint: chanState.FundingOutpoint,
Expand All @@ -116,6 +123,7 @@ func NewAuxChanState(chanState *channeldb.OpenChannel) AuxChanState {
RemoteChanCfg: chanState.RemoteChanCfg,
ThawHeight: chanState.ThawHeight,
TapscriptRoot: chanState.TapscriptRoot,
PeerPubKey: route.Vertex(peerPub),
CustomBlob: chanState.CustomBlob,
}
}
Expand Down
34 changes: 34 additions & 0 deletions lnwallet/aux_negotiator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package lnwallet

import (
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)

// AuxChannelNegotiator is an interface that allows aux channel implementations
// to inject or handle custom records in the init message that is used when
// establishing a connection with a peer. It may also notify the aux channel
// implementation for the channel ready or channel reestablish events, which
// mark the channel as ready to use.
type AuxChannelNegotiator interface {
// GetInitRecords is called when sending an init message to a peer.
// It returns custom records to include in the init message TLVs. The
// implementation can decide which records to include based on the peer
// identity.
GetInitRecords(peer route.Vertex) (lnwire.CustomRecords, error)

// ProcessInitRecords handles received init records from a peer. The
// implementation can store state internally to affect future
// channel operations with this peer.
ProcessInitRecords(peer route.Vertex,
customRecords lnwire.CustomRecords) error

// ProcessChannelReady handles the event of marking a channel identified
// by its channel ID as ready to use. We also provide the peer the
// channel was established with.
ProcessChannelReady(cid lnwire.ChannelID, peer route.Vertex)

// ProcessReestablish handles the received channel_reestablish message
// which marks a channel identified by its cid as ready to use again.
ProcessReestablish(cid lnwire.ChannelID, peer route.Vertex)
}
33 changes: 29 additions & 4 deletions lnwire/init_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type Init struct {
// Features field.
Features *RawFeatureVector

// CustomRecords maps TLV types to byte slices, storing arbitrary data
// intended for inclusion in the ExtraData field of the init message.
CustomRecords CustomRecords

// ExtraData 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.
Expand All @@ -35,7 +39,6 @@ func NewInitMessage(gf *RawFeatureVector, f *RawFeatureVector) *Init {
return &Init{
GlobalFeatures: gf,
Features: f,
ExtraData: make([]byte, 0),
}
}

Expand All @@ -52,11 +55,28 @@ var _ SizeableMessage = (*Init)(nil)
//
// This is part of the lnwire.Message interface.
func (msg *Init) Decode(r io.Reader, pver uint32) error {
return ReadElements(r,
var msgExtraData ExtraOpaqueData

err := ReadElements(r,
&msg.GlobalFeatures,
&msg.Features,
&msg.ExtraData,
&msgExtraData,
)
if err != nil {
return err
}

customRecords, _, extraDData, err := ParseAndExtractCustomRecords(
msgExtraData,
)
if err != nil {
return err
}

msg.CustomRecords = customRecords
msg.ExtraData = extraDData

return nil
}

// Encode serializes the target Init into the passed io.Writer observing
Expand All @@ -72,7 +92,12 @@ func (msg *Init) Encode(w *bytes.Buffer, pver uint32) error {
return err
}

return WriteBytes(w, msg.ExtraData)
extraData, err := MergeAndEncode(nil, msg.ExtraData, msg.CustomRecords)
if err != nil {
return err
}

return WriteBytes(w, extraData)
}

// MsgType returns the integer uniquely identifying this message type on the
Expand Down
61 changes: 61 additions & 0 deletions lnwire/init_message_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package lnwire

import (
"bytes"
"testing"

"github.com/stretchr/testify/require"
)

// TestInitEncodeDecode checks that we can encode and decode an Init message
// to and from a byte stream.
func TestInitEncodeDecode(t *testing.T) {
t.Parallel()

// These are the raw bytes that we expect to be generated from the
// sample Init message.
rawBytes := []byte{
// GlobalFeatures
0x00, 0x01, 0xc0,

// Features
0x00, 0x01, 0xc0,

// ExtraData - unknown odd-type TLV record.
0x6f, // type (111)
0x02, // length
0x79, 0x79, // value

// ExtraData - custom TLV record.
// TLV record for type 67676
0xfe, 0x00, 0x01, 0x08, 0x6c, // type (67676)
0x05, // length
0x01, 0x02, 0x03, 0x04, 0x05, // value

// ExtraData - custom TLV record.
// TLV record for type 67777
0xfe, 0x00, 0x01, 0x08, 0xc1, // type (67777)
0x03, // length
0x01, 0x02, 0x03, // value
}

// Create a new empty message and decode the raw bytes into it.
msg := &Init{}
r := bytes.NewReader(rawBytes)
err := msg.Decode(r, 0)
require.NoError(t, err)

require.NotNil(t, msg.GlobalFeatures)
require.NotNil(t, msg.Features)
require.NotNil(t, msg.CustomRecords)
require.NotNil(t, msg.ExtraData)

// Next, encode the message back into a new byte buffer.
var b bytes.Buffer
err = msg.Encode(&b, 0)
require.NoError(t, err)

// The re-encoded bytes should be exactly the same as the original raw
// bytes.
require.Equal(t, rawBytes, b.Bytes())
}
4 changes: 4 additions & 0 deletions lnwire/test_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,10 @@ func (msg *Init) RandTestMessage(t *rapid.T) Message {
local.Set(bit)
}

ignoreRecords := fn.NewSet[uint64]()

msg.ExtraData = RandExtraOpaqueData(t, ignoreRecords)

return NewInitMessage(global, local)
}

Expand Down
47 changes: 45 additions & 2 deletions peer/brontide.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,11 @@ type Config struct {
// used to modify the way the co-op close transaction is constructed.
AuxChanCloser fn.Option[chancloser.AuxChanCloser]

// AuxChannelNegotiator is an optional interface that allows aux channel
// implementations to inject and process custom records over channel
// related wire messages.
AuxChannelNegotiator fn.Option[lnwallet.AuxChannelNegotiator]

// ShouldFwdExpEndorsement is a closure that indicates whether
// experimental endorsement signals should be set.
ShouldFwdExpEndorsement func() bool
Expand Down Expand Up @@ -1454,8 +1459,9 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
ShouldFwdExpEndorsement: p.cfg.ShouldFwdExpEndorsement,
DisallowQuiescence: p.cfg.DisallowQuiescence ||
!p.remoteFeatures.HasFeature(lnwire.QuiescenceOptional),
AuxTrafficShaper: p.cfg.AuxTrafficShaper,
QuiescenceTimeout: p.cfg.QuiescenceTimeout,
AuxTrafficShaper: p.cfg.AuxTrafficShaper,
AuxChannelNegotiator: p.cfg.AuxChannelNegotiator,
QuiescenceTimeout: p.cfg.QuiescenceTimeout,
}

// Before adding our new link, purge the switch of any pending or live
Expand Down Expand Up @@ -4537,6 +4543,19 @@ func (p *Brontide) handleInitMsg(msg *lnwire.Init) error {
return fmt.Errorf("data loss protection required")
}

// If we have an AuxChannelNegotiator and the peer sent aux features,
// process them.
p.cfg.AuxChannelNegotiator.WhenSome(
func(acn lnwallet.AuxChannelNegotiator) {
err = acn.ProcessInitRecords(
p.cfg.PubKeyBytes, msg.CustomRecords.Copy(),
)
},
)
if err != nil {
return fmt.Errorf("could not process init records: %w", err)
}

return nil
}

Expand Down Expand Up @@ -4597,6 +4616,30 @@ func (p *Brontide) sendInitMsg(legacyChan bool) error {
features.RawFeatureVector,
)

var err error

// If we have an AuxChannelNegotiator, get custom feature bits to
// include in the init message.
p.cfg.AuxChannelNegotiator.WhenSome(
func(negotiator lnwallet.AuxChannelNegotiator) {
var auxRecords lnwire.CustomRecords
auxRecords, err = negotiator.GetInitRecords(
p.cfg.PubKeyBytes,
)
if err != nil {
p.log.Warnf("Failed to get aux init features: "+
"%v", err)
return
}

mergedRecs := msg.CustomRecords.MergedCopy(auxRecords)
msg.CustomRecords = mergedRecs
},
)
if err != nil {
return err
}

return p.writeMessage(msg)
}

Expand Down
2 changes: 2 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
AuxFundingController: implCfg.AuxFundingController,
AuxSigner: implCfg.AuxSigner,
AuxResolver: implCfg.AuxContractResolver,
AuxChannelNegotiator: implCfg.AuxChannelNegotiator,
})
if err != nil {
return nil, err
Expand Down Expand Up @@ -4431,6 +4432,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
AuxChanCloser: s.implCfg.AuxChanCloser,
AuxResolver: s.implCfg.AuxContractResolver,
AuxTrafficShaper: s.implCfg.TrafficShaper,
AuxChannelNegotiator: s.implCfg.AuxChannelNegotiator,
ShouldFwdExpEndorsement: func() bool {
if s.cfg.ProtocolOptions.NoExperimentalEndorsement() {
return false
Expand Down
Loading