Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Experimental Endorsement Signalling #8390

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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: 4 additions & 1 deletion contractcourt/htlc_incoming_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ func (h *htlcIncomingContestResolver) Resolve(
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
if len(h.htlc.CustomRecords) != 0 && h.isTapscriptRoot {
log.Warnf("Not resolving HTLC with: %v custom records",
len(h.htlc.CustomRecords))

select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
Expand Down
5 changes: 4 additions & 1 deletion contractcourt/htlc_outgoing_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ func (h *htlcOutgoingContestResolver) Resolve(
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
if len(h.htlc.CustomRecords) != 0 && h.isTapscriptRoot {
log.Warnf("Not resolving HTLC with: %v custom records",
len(h.htlc.CustomRecords))

select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
Expand Down
22 changes: 21 additions & 1 deletion contractcourt/htlc_success_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ type htlcSuccessResolver struct {
// htlc contains information on the htlc that we are resolving on-chain.
htlc channeldb.HTLC

// isTapscriptRoot indicates whether the htlc is on a TapscriptRoot
// channel type.
// TODO: remove this when incoming HTLCs with custom records can be
// resolved for this channel type
isTapscriptRoot bool

// currentReport stores the current state of the resolver for reporting
// over the rpc interface. This should only be reported in case we have
// a non-nil SignDetails on the htlcResolution, otherwise the nursery
Expand Down Expand Up @@ -127,7 +133,10 @@ func (h *htlcSuccessResolver) Resolve(
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
if len(h.htlc.CustomRecords) != 0 && h.isTapscriptRoot {
log.Warnf("Not resolving incoming htlc with %v custom records",
len(h.htlc.CustomRecords))

select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
Expand Down Expand Up @@ -739,6 +748,17 @@ func (h *htlcSuccessResolver) HtlcPoint() wire.OutPoint {
func (h *htlcSuccessResolver) SupplementDeadline(_ fn.Option[int32]) {
}

// SupplementState allows the user of a ContractResolver to supplement it with
// state required for the proper resolution of a contract.
//
// NOTE: Part of the ContractResolver interface.
func (h *htlcIncomingContestResolver) SupplementState(
state *channeldb.OpenChannel) {

h.isTapscriptRoot = state.ChanType.HasTapscriptRoot()
h.htlcLeaseResolver.SupplementState(state)
}

// A compile time assertion to ensure htlcSuccessResolver meets the
// ContractResolver interface.
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
20 changes: 19 additions & 1 deletion contractcourt/htlc_timeout_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ type htlcTimeoutResolver struct {
// htlc contains information on the htlc that we are resolving on-chain.
htlc channeldb.HTLC

// isTapscriptRoot indicates whether the htlc is on a TapscriptRoot
// channel type.
// TODO: remove this when incoming HTLCs with custom records can be
// resolved for this channel type
isTapscriptRoot bool

// currentReport stores the current state of the resolver for reporting
// over the rpc interface. This should only be reported in case we have
// a non-nil SignDetails on the htlcResolution, otherwise the nursery
Expand Down Expand Up @@ -430,7 +436,10 @@ func (h *htlcTimeoutResolver) Resolve(
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
if len(h.htlc.CustomRecords) != 0 && h.isTapscriptRoot {
log.Warnf("Not resolving incoming htlc with %v custom records",
len(h.htlc.CustomRecords))

select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
Expand Down Expand Up @@ -1095,6 +1104,15 @@ func (h *htlcTimeoutResolver) SupplementDeadline(d fn.Option[int32]) {
h.incomingHTLCExpiryHeight = d
}

// SupplementState allows the user of a ContractResolver to supplement it with
// state required for the proper resolution of a contract.
//
// NOTE: Part of the ContractResolver interface.
func (h *htlcTimeoutResolver) SupplementState(state *channeldb.OpenChannel) {
h.isTapscriptRoot = state.ChanType.HasTapscriptRoot()
h.htlcLeaseResolver.SupplementState(state)
}

// A compile time assertion to ensure htlcTimeoutResolver meets the
// ContractResolver interface.
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
Expand Down
7 changes: 7 additions & 0 deletions docs/release-notes/release-notes-0.19.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
cases.

# New Features

* [Support](https://github.com/lightningnetwork/lnd/pull/8390) for
[experimental endorsement](https://github.com/lightning/blips/pull/27)
signal relay was added. This signal has *no impact* on routing, and
is deployed experimentally to assist ongoing channel jamming research.

## Functional Enhancements
## RPC Additions

Expand Down Expand Up @@ -173,6 +179,7 @@

* Animesh Bilthare
* Boris Nagaev
* Carla Kirk-Cohen
* CharlieZKSmith
* Elle Mouton
* George Tsagkarelis
Expand Down
3 changes: 3 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,7 @@ var defaultSetDesc = setDesc{
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.ExperimentalEndorsementOptional: {
SetNodeAnn: {}, // N
},
}
10 changes: 10 additions & 0 deletions feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ type Config struct {
// NoTaprootOverlay unsets the taproot overlay channel feature bits.
NoTaprootOverlay bool

// NoExperimentalEndorsement unsets any bits that signal support for
// forwarding experimental endorsement.
NoExperimentalEndorsement bool

// CustomFeatures is a set of custom features to advertise in each
// set.
CustomFeatures map[Set][]lnwire.FeatureBit
Expand Down Expand Up @@ -199,6 +203,12 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
}

if cfg.NoExperimentalEndorsement {
raw.Unset(lnwire.ExperimentalEndorsementOptional)
raw.Unset(lnwire.ExperimentalEndorsementRequired)
carlaKC marked this conversation as resolved.
Show resolved Hide resolved
}

for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() {
return nil, fmt.Errorf("feature bit: %v "+
Expand Down
9 changes: 6 additions & 3 deletions htlcswitch/interceptable_switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,9 +715,12 @@ func (f *interceptedForward) ResumeModified(
htlc.Amount = amount
})

if len(validatedRecords) > 0 {
htlc.CustomRecords = validatedRecords
}
// Merge custom records with any validated records that were
// added in the modify request, overwriting any existing values
// with those supplied in the modifier API.
htlc.CustomRecords = htlc.CustomRecords.MergedCopy(
validatedRecords,
)

case *lnwire.UpdateFulfillHTLC:
if len(validatedRecords) > 0 {
Expand Down
66 changes: 66 additions & 0 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/ticker"
"github.com/lightningnetwork/lnd/tlv"
)
Expand Down Expand Up @@ -285,6 +286,10 @@ type ChannelLinkConfig struct {
// MaxFeeExposure is the threshold in milli-satoshis after which we'll
// restrict the flow of HTLCs and fee updates.
MaxFeeExposure lnwire.MilliSatoshi

// ShouldFwdExpEndorsement is a closure that indicates whether the link
// should forward experimental endorsement signals.
ShouldFwdExpEndorsement func() bool
}

// channelLink is the service which drives a channel's commitment update
Expand Down Expand Up @@ -3624,6 +3629,13 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) {
continue
}

endorseValue := l.experimentalEndorsement(
record.CustomSet(add.CustomRecords),
)
endorseType := uint64(
lnwire.ExperimentalEndorsementType,
)

switch fwdPkg.State {
case channeldb.FwdStateProcessed:
// This add was not forwarded on the previous
Expand All @@ -3645,6 +3657,14 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) {
BlindingPoint: fwdInfo.NextBlinding,
}

endorseValue.WhenSome(func(e byte) {
custRecords := map[uint64][]byte{
endorseType: {e},
}

outgoingAdd.CustomRecords = custRecords
})

// Finally, we'll encode the onion packet for
// the _next_ hop using the hop iterator
// decoded for the current hop.
Expand Down Expand Up @@ -3695,6 +3715,12 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) {
BlindingPoint: fwdInfo.NextBlinding,
}

endorseValue.WhenSome(func(e byte) {
addMsg.CustomRecords = map[uint64][]byte{
endorseType: {e},
}
})

// Finally, we'll encode the onion packet for the
// _next_ hop using the hop iterator decoded for the
// current hop.
Expand Down Expand Up @@ -3782,6 +3808,46 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) {
l.forwardBatch(replay, switchPackets...)
}

// experimentalEndorsement returns the value to set for our outgoing
// experimental endorsement field, and a boolean indicating whether it should
// be populated on the outgoing htlc.
func (l *channelLink) experimentalEndorsement(
carlaKC marked this conversation as resolved.
Show resolved Hide resolved
customUpdateAdd record.CustomSet) fn.Option[byte] {

// Only relay experimental signal if we are within the experiment
// period.
if !l.cfg.ShouldFwdExpEndorsement() {
return fn.None[byte]()
}

// If we don't have any custom records or the experimental field is
// not set, just forward a zero value.
if len(customUpdateAdd) == 0 {
return fn.Some[byte](lnwire.ExperimentalUnendorsed)
}

t := uint64(lnwire.ExperimentalEndorsementType)
value, set := customUpdateAdd[t]
if !set {
return fn.Some[byte](lnwire.ExperimentalUnendorsed)
}

// We expect at least one byte for this field, consider it invalid if
// it has no data and just forward a zero value.
if len(value) == 0 {
carlaKC marked this conversation as resolved.
Show resolved Hide resolved
return fn.Some[byte](lnwire.ExperimentalUnendorsed)
}

// Only forward endorsed if the incoming link is endorsed.
if value[0] == lnwire.ExperimentalEndorsed {
return fn.Some[byte](lnwire.ExperimentalEndorsed)
}

// Forward as unendorsed otherwise, including cases where we've
// received an invalid value that uses more than 3 bits of information.
return fn.Some[byte](lnwire.ExperimentalUnendorsed)
}

// processExitHop handles an htlc for which this link is the exit hop. It
// returns a boolean indicating whether the commitment tx needs an update.
func (l *channelLink) processExitHop(add lnwire.UpdateAddHTLC,
Expand Down
4 changes: 4 additions & 0 deletions htlcswitch/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2245,6 +2245,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
NotifyInactiveLinkEvent: func(wire.OutPoint) {},
HtlcNotifier: aliceSwitch.cfg.HtlcNotifier,
GetAliases: getAliases,
ShouldFwdExpEndorsement: func() bool { return true },
}

aliceLink := NewChannelLink(aliceCfg, aliceLc.channel)
Expand Down Expand Up @@ -4888,6 +4889,8 @@ func (h *persistentLinkHarness) restartLink(
// Instantiate with a long interval, so that we can precisely control
// the firing via force feeding.
bticker := ticker.NewForce(time.Hour)

//nolint:lll
aliceCfg := ChannelLinkConfig{
FwrdingPolicy: globalPolicy,
Peer: alicePeer,
Expand Down Expand Up @@ -4932,6 +4935,7 @@ func (h *persistentLinkHarness) restartLink(
HtlcNotifier: h.hSwitch.cfg.HtlcNotifier,
SyncStates: syncStates,
GetAliases: getAliases,
ShouldFwdExpEndorsement: func() bool { return true },
}

aliceLink := NewChannelLink(aliceCfg, aliceChannel)
Expand Down
2 changes: 2 additions & 0 deletions htlcswitch/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
return server.htlcSwitch.ForwardPackets(linkQuit, packets...)
}

//nolint:lll
ProofOfKeags marked this conversation as resolved.
Show resolved Hide resolved
link := NewChannelLink(
ChannelLinkConfig{
BestHeight: server.htlcSwitch.BestHeight,
Expand Down Expand Up @@ -1193,6 +1194,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
NotifyInactiveLinkEvent: func(wire.OutPoint) {},
HtlcNotifier: server.htlcSwitch.cfg.HtlcNotifier,
GetAliases: getAliases,
ShouldFwdExpEndorsement: func() bool { return true },
},
channel,
)
Expand Down
4 changes: 4 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,4 +702,8 @@ var allTestCases = []*lntest.TestCase{
Name: "send to route failed htlc timeout",
TestFunc: testSendToRouteFailHTLCTimeout,
},
{
Name: "experimental endorsement",
TestFunc: testExperimentalEndorsement,
},
}
Loading
Loading