mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-13 12:32:48 +02:00
Extend the on-chain interceptor path in the witness beacon to expose a node-ID next hop, mirroring the off-chain path. A node-ID next hop has no outgoing channel of its own, so the beacon reports hop.Exit as the outgoing channel (via ForwardingInfo.NextHopChannel().UnwrapOr) and the requested next node's public key. The RPC boundary maps that to the NodeIDForwardSCID sentinel so the forward is not misread as a final receive. This is the requested next hop, not the channel eventually selected by non-strict forwarding, so the beacon deliberately does not resolve it against the circuit map.
150 lines
3.6 KiB
Go
150 lines
3.6 KiB
Go
package lnd
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/lightningnetwork/lnd/chanstate"
|
|
"github.com/lightningnetwork/lnd/fn/v2"
|
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestWitnessBeaconIntercept tests that the beacon passes on subscriptions to
|
|
// the interceptor correctly.
|
|
func TestWitnessBeaconIntercept(t *testing.T) {
|
|
var interceptedFwd htlcswitch.InterceptedForward
|
|
interceptor := func(fwd htlcswitch.InterceptedForward) error {
|
|
interceptedFwd = fwd
|
|
|
|
return nil
|
|
}
|
|
var canceledKey models.CircuitKey
|
|
cancelInterceptor := func(key models.CircuitKey) error {
|
|
canceledKey = key
|
|
|
|
return nil
|
|
}
|
|
|
|
p := newPreimageBeacon(
|
|
&mockWitnessCache{}, interceptor, cancelInterceptor,
|
|
)
|
|
|
|
preimage := lntypes.Preimage{1, 2, 3}
|
|
hash := preimage.Hash()
|
|
|
|
subscription, err := p.SubscribeUpdates(
|
|
lnwire.NewShortChanIDFromInt(1),
|
|
&chanstate.HTLC{
|
|
RHash: hash,
|
|
},
|
|
&hop.Payload{},
|
|
[]byte{2},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, interceptedFwd.Settle(preimage))
|
|
|
|
update := <-subscription.WitnessUpdates
|
|
require.Equal(t, preimage, update)
|
|
|
|
subscription.CancelSubscription()
|
|
require.Equal(t, interceptedFwd.Packet().IncomingCircuit, canceledKey)
|
|
}
|
|
|
|
// TestWitnessBeaconInterceptErrorCancels tests that a failed interceptor offer
|
|
// tears down the witness subscription and on-chain intercept handle.
|
|
func TestWitnessBeaconInterceptErrorCancels(t *testing.T) {
|
|
errInterceptor := errors.New("interceptor error")
|
|
|
|
interceptor := func(htlcswitch.InterceptedForward) error {
|
|
return errInterceptor
|
|
}
|
|
|
|
var canceledKey models.CircuitKey
|
|
cancelInterceptor := func(key models.CircuitKey) error {
|
|
canceledKey = key
|
|
|
|
return nil
|
|
}
|
|
|
|
p := newPreimageBeacon(
|
|
&mockWitnessCache{}, interceptor, cancelInterceptor,
|
|
)
|
|
|
|
chanID := lnwire.NewShortChanIDFromInt(1)
|
|
htlc := &chanstate.HTLC{
|
|
HtlcIndex: 2,
|
|
RHash: lntypes.Hash{3},
|
|
}
|
|
|
|
subscription, err := p.SubscribeUpdates(
|
|
chanID, htlc, &hop.Payload{}, []byte{2},
|
|
)
|
|
require.ErrorIs(t, err, errInterceptor)
|
|
require.Nil(t, subscription)
|
|
|
|
require.Equal(t, models.CircuitKey{
|
|
ChanID: chanID,
|
|
HtlcID: htlc.HtlcIndex,
|
|
}, canceledKey)
|
|
|
|
p.RLock()
|
|
require.Empty(t, p.subscribers)
|
|
p.RUnlock()
|
|
}
|
|
|
|
// TestWitnessBeaconInterceptNodeID asserts that for a node-ID next hop the
|
|
// on-chain interceptor reports the exit-hop SCID (hop.Exit) together with the
|
|
// requested next node's public key, matching the off-chain interceptor. The
|
|
// next hop is not resolved against the circuit map; the RPC boundary maps
|
|
// hop.Exit to the sentinel.
|
|
func TestWitnessBeaconInterceptNodeID(t *testing.T) {
|
|
var interceptedFwd htlcswitch.InterceptedForward
|
|
interceptor := func(fwd htlcswitch.InterceptedForward) error {
|
|
interceptedFwd = fwd
|
|
|
|
return nil
|
|
}
|
|
|
|
p := newPreimageBeacon(
|
|
&mockWitnessCache{}, interceptor,
|
|
func(models.CircuitKey) error {
|
|
return nil
|
|
},
|
|
)
|
|
|
|
var nodeID [33]byte
|
|
nodeID[0] = 0x02
|
|
|
|
payload := &hop.Payload{
|
|
FwdInfo: hop.ForwardingInfo{
|
|
NextHop: hop.NewNodeNextHop(nodeID),
|
|
},
|
|
}
|
|
|
|
_, err := p.SubscribeUpdates(
|
|
lnwire.NewShortChanIDFromInt(1),
|
|
&chanstate.HTLC{RHash: lntypes.Hash{1}},
|
|
payload, []byte{2},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
packet := interceptedFwd.Packet()
|
|
require.Equal(t, hop.Exit, packet.OutgoingChanID)
|
|
require.Equal(t, fn.Some(nodeID), packet.OutgoingNodeID)
|
|
}
|
|
|
|
type mockWitnessCache struct {
|
|
witnessCache
|
|
}
|
|
|
|
func (w *mockWitnessCache) AddSha256Witnesses(
|
|
preimages ...lntypes.Preimage) error {
|
|
|
|
return nil
|
|
}
|