mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-26 08:55:59 +01:00
rpcserver+invoicesrpc: alias-aware AddInvoice,AddHoldInvoice rpc
AddInvoice,AddHoldInvoice now issue invoices that include our peer's aliases. Some extra sanity checks are included to ensure we don't leak our confirmed SCID for a private channel.
This commit is contained in:
parent
1aa9626606
commit
0ba67015da
6 changed files with 117 additions and 17 deletions
|
@ -72,6 +72,10 @@ type AddInvoiceConfig struct {
|
|||
// GenAmpInvoiceFeatures returns a feature containing feature bits that
|
||||
// should be advertised on freshly generated AMP invoices.
|
||||
GenAmpInvoiceFeatures func() *lnwire.FeatureVector
|
||||
|
||||
// GetAlias allows the peer's alias SCID to be retrieved for private
|
||||
// option_scid_alias channels.
|
||||
GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)
|
||||
}
|
||||
|
||||
// AddInvoiceData contains the required data to create a new invoice.
|
||||
|
@ -387,9 +391,27 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||
continue
|
||||
}
|
||||
|
||||
// If this is a zero-conf channel, check if the
|
||||
// confirmed SCID was used in forcedHints.
|
||||
realScid := c.ZeroConfRealScid().ToUint64()
|
||||
if c.IsZeroConf() {
|
||||
if _, ok := forcedHints[realScid]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
chanID := lnwire.NewChanIDFromOutPoint(
|
||||
&c.FundingOutpoint,
|
||||
)
|
||||
|
||||
// Check whether the the peer's alias was
|
||||
// provided in forcedHints.
|
||||
peerAlias, _ := cfg.GetAlias(chanID)
|
||||
peerScid := peerAlias.ToUint64()
|
||||
if _, ok := forcedHints[peerScid]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
isActive := cfg.IsChannelActive(chanID)
|
||||
|
||||
hopHintInfo := newHopHintInfo(c, isActive)
|
||||
|
@ -529,10 +551,17 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
|
|||
// Fetch the policies for each end of the channel.
|
||||
info, p1, p2, err := cfg.FetchChannelEdgesByID(channel.ShortChannelID)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to fetch the routing "+
|
||||
"policies for the edges of the channel "+
|
||||
"%v: %v", channel.ShortChannelID, err)
|
||||
return nil, false
|
||||
// In the case of zero-conf channels, it may be the case that
|
||||
// the alias SCID was deleted from the graph, and replaced by
|
||||
// the confirmed SCID. Check the Graph for the confirmed SCID.
|
||||
confirmedScid := channel.ConfirmedScidZC
|
||||
info, p1, p2, err = cfg.FetchChannelEdgesByID(confirmedScid)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to fetch the routing policies for "+
|
||||
"the edges of the channel %v: %v",
|
||||
channel.ShortChannelID, err)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Now, we'll need to determine which is the correct policy for HTLCs
|
||||
|
@ -550,7 +579,8 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
|
|||
// addHopHint creates a hop hint out of the passed channel and channel policy.
|
||||
// The new hop hint is appended to the passed slice.
|
||||
func addHopHint(hopHints *[][]zpay32.HopHint,
|
||||
channel *HopHintInfo, chanPolicy *channeldb.ChannelEdgePolicy) {
|
||||
channel *HopHintInfo, chanPolicy *channeldb.ChannelEdgePolicy,
|
||||
aliasScid lnwire.ShortChannelID) {
|
||||
|
||||
hopHint := zpay32.HopHint{
|
||||
NodeID: channel.RemotePubkey,
|
||||
|
@ -562,6 +592,11 @@ func addHopHint(hopHints *[][]zpay32.HopHint,
|
|||
CLTVExpiryDelta: chanPolicy.TimeLockDelta,
|
||||
}
|
||||
|
||||
var defaultScid lnwire.ShortChannelID
|
||||
if aliasScid != defaultScid {
|
||||
hopHint.ChannelID = aliasScid.ToUint64()
|
||||
}
|
||||
|
||||
*hopHints = append(*hopHints, []zpay32.HopHint{hopHint})
|
||||
}
|
||||
|
||||
|
@ -587,18 +622,28 @@ type HopHintInfo struct {
|
|||
|
||||
// ShortChannelID is the short channel ID of the channel.
|
||||
ShortChannelID uint64
|
||||
|
||||
// ConfirmedScidZC is the confirmed SCID of a zero-conf channel. This
|
||||
// may be used for looking up a channel in the graph.
|
||||
ConfirmedScidZC uint64
|
||||
|
||||
// ScidAliasFeature denotes whether the channel has negotiated the
|
||||
// option-scid-alias feature bit.
|
||||
ScidAliasFeature bool
|
||||
}
|
||||
|
||||
func newHopHintInfo(c *channeldb.OpenChannel, isActive bool) *HopHintInfo {
|
||||
isPublic := c.ChannelFlags&lnwire.FFAnnounceChannel != 0
|
||||
|
||||
return &HopHintInfo{
|
||||
IsPublic: isPublic,
|
||||
IsActive: isActive,
|
||||
FundingOutpoint: c.FundingOutpoint,
|
||||
RemotePubkey: c.IdentityPub,
|
||||
RemoteBalance: c.LocalCommitment.RemoteBalance,
|
||||
ShortChannelID: c.ShortChannelID.ToUint64(),
|
||||
IsPublic: isPublic,
|
||||
IsActive: isActive,
|
||||
FundingOutpoint: c.FundingOutpoint,
|
||||
RemotePubkey: c.IdentityPub,
|
||||
RemoteBalance: c.LocalCommitment.RemoteBalance,
|
||||
ShortChannelID: c.ShortChannelID.ToUint64(),
|
||||
ConfirmedScidZC: c.ZeroConfRealScid().ToUint64(),
|
||||
ScidAliasFeature: c.ChanType.HasScidAliasFeature(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,12 +660,17 @@ type SelectHopHintsCfg struct {
|
|||
FetchChannelEdgesByID func(chanID uint64) (*channeldb.ChannelEdgeInfo,
|
||||
*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy,
|
||||
error)
|
||||
|
||||
// GetAlias allows the peer's alias SCID to be retrieved for private
|
||||
// option_scid_alias channels.
|
||||
GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)
|
||||
}
|
||||
|
||||
func newSelectHopHintsCfg(invoicesCfg *AddInvoiceConfig) *SelectHopHintsCfg {
|
||||
return &SelectHopHintsCfg{
|
||||
IsPublicNode: invoicesCfg.Graph.IsPublicNode,
|
||||
FetchChannelEdgesByID: invoicesCfg.Graph.FetchChannelEdgesByID,
|
||||
GetAlias: invoicesCfg.GetAlias,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -693,9 +743,26 @@ func SelectHopHints(amtMSat lnwire.MilliSatoshi, cfg *SelectHopHintsCfg,
|
|||
continue
|
||||
}
|
||||
|
||||
// Lookup and see if there is an alias SCID that exists.
|
||||
chanID := lnwire.NewChanIDFromOutPoint(
|
||||
&channel.FundingOutpoint,
|
||||
)
|
||||
alias, _ := cfg.GetAlias(chanID)
|
||||
|
||||
// If this is a channel where the option-scid-alias feature bit
|
||||
// was negotiated and the alias is not yet assigned, we cannot
|
||||
// issue an invoice. Doing so might expose the confirmed SCID
|
||||
// of a private channel.
|
||||
if channel.ScidAliasFeature {
|
||||
var defaultScid lnwire.ShortChannelID
|
||||
if alias == defaultScid {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we now this channel use usable, add it as a hop
|
||||
// hint and the indexes we'll use later.
|
||||
addHopHint(&hopHints, channel, edgePolicy)
|
||||
addHopHint(&hopHints, channel, edgePolicy, alias)
|
||||
|
||||
hopHintChans[channel.FundingOutpoint] = struct{}{}
|
||||
totalHintBandwidth += channel.RemoteBalance
|
||||
|
@ -733,9 +800,26 @@ func SelectHopHints(amtMSat lnwire.MilliSatoshi, cfg *SelectHopHintsCfg,
|
|||
continue
|
||||
}
|
||||
|
||||
// Lookup and see if there's an alias SCID that exists.
|
||||
chanID := lnwire.NewChanIDFromOutPoint(
|
||||
&channel.FundingOutpoint,
|
||||
)
|
||||
alias, _ := cfg.GetAlias(chanID)
|
||||
|
||||
// If this is a channel where the option-scid-alias feature bit
|
||||
// was negotiated and the alias is not yet assigned, we cannot
|
||||
// issue an invoice. Doing so might expose the confirmed SCID
|
||||
// of a private channel.
|
||||
if channel.ScidAliasFeature {
|
||||
var defaultScid lnwire.ShortChannelID
|
||||
if alias == defaultScid {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Include the route hint in our set of options that will be
|
||||
// used when creating the invoice.
|
||||
addHopHint(&hopHints, channel, remotePolicy)
|
||||
addHopHint(&hopHints, channel, remotePolicy, alias)
|
||||
|
||||
// As we've just added a new hop hint, we'll accumulate it's
|
||||
// available balance now to update our tally.
|
||||
|
|
|
@ -274,11 +274,11 @@ func TestSelectHopHints(t *testing.T) {
|
|||
// hop hint.
|
||||
h.Mock.On(
|
||||
"FetchChannelEdgesByID",
|
||||
private1ShortID,
|
||||
mock.Anything,
|
||||
).Return(
|
||||
nil, nil, nil,
|
||||
errors.New("no edge"),
|
||||
)
|
||||
).Times(4)
|
||||
},
|
||||
amount: 100,
|
||||
channels: []*HopHintInfo{
|
||||
|
@ -536,6 +536,10 @@ func TestSelectHopHints(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
getAlias := func(lnwire.ChannelID) (lnwire.ShortChannelID, error) {
|
||||
return lnwire.ShortChannelID{}, nil
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
|
@ -548,6 +552,7 @@ func TestSelectHopHints(t *testing.T) {
|
|||
cfg := &SelectHopHintsCfg{
|
||||
IsPublicNode: mock.IsPublicNode,
|
||||
FetchChannelEdgesByID: mock.FetchChannelEdgesByID,
|
||||
GetAlias: getAlias,
|
||||
}
|
||||
|
||||
hints := SelectHopHints(
|
||||
|
|
|
@ -60,4 +60,8 @@ type Config struct {
|
|||
// GenAmpInvoiceFeatures returns a feature containing feature bits that
|
||||
// should be advertised on freshly generated AMP invoices.
|
||||
GenAmpInvoiceFeatures func() *lnwire.FeatureVector
|
||||
|
||||
// GetAlias returns the peer's alias SCID if it exists given the
|
||||
// 32-byte ChannelID.
|
||||
GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)
|
||||
}
|
||||
|
|
|
@ -331,6 +331,7 @@ func (s *Server) AddHoldInvoice(ctx context.Context,
|
|||
Graph: s.cfg.GraphDB,
|
||||
GenInvoiceFeatures: s.cfg.GenInvoiceFeatures,
|
||||
GenAmpInvoiceFeatures: s.cfg.GenAmpInvoiceFeatures,
|
||||
GetAlias: s.cfg.GetAlias,
|
||||
}
|
||||
|
||||
hash, err := lntypes.MakeHash(invoice.Hash)
|
||||
|
|
|
@ -756,6 +756,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|||
r.cfg.net.ResolveTCPAddr, genInvoiceFeatures,
|
||||
genAmpInvoiceFeatures, getNodeAnnouncement,
|
||||
s.updateAndBrodcastSelfNode, parseAddr, rpcsLog,
|
||||
s.aliasMgr.GetPeerAlias,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -3039,7 +3040,7 @@ func (r *rpcServer) SubscribePeerEvents(req *lnrpc.PeerEventSubscription,
|
|||
// confirmed unspent outputs and all unconfirmed unspent outputs under control
|
||||
// by the wallet. This method can be modified by having the request specify
|
||||
// only witness outputs should be factored into the final output sum.
|
||||
// TODO(roasbeef): add async hooks into wallet balance changes
|
||||
// TODO(roasbeef): add async hooks into wallet balance changes.
|
||||
func (r *rpcServer) WalletBalance(ctx context.Context,
|
||||
in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) {
|
||||
|
||||
|
@ -5260,6 +5261,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
|
|||
GenAmpInvoiceFeatures: func() *lnwire.FeatureVector {
|
||||
return r.server.featureMgr.Get(feature.SetInvoiceAmp)
|
||||
},
|
||||
GetAlias: r.server.aliasMgr.GetPeerAlias,
|
||||
}
|
||||
|
||||
value, err := lnrpc.UnmarshallAmt(invoice.Value, invoice.ValueMsat)
|
||||
|
|
|
@ -121,7 +121,8 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
|||
getNodeAnnouncement func() (lnwire.NodeAnnouncement, error),
|
||||
updateNodeAnnouncement func(modifiers ...netann.NodeAnnModifier) error,
|
||||
parseAddr func(addr string) (net.Addr, error),
|
||||
rpcLogger btclog.Logger) error {
|
||||
rpcLogger btclog.Logger,
|
||||
getAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)) error {
|
||||
|
||||
// First, we'll use reflect to obtain a version of the config struct
|
||||
// that allows us to programmatically inspect its fields.
|
||||
|
@ -257,6 +258,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
|||
subCfgValue.FieldByName("GenAmpInvoiceFeatures").Set(
|
||||
reflect.ValueOf(genAmpInvoiceFeatures),
|
||||
)
|
||||
subCfgValue.FieldByName("GetAlias").Set(
|
||||
reflect.ValueOf(getAlias),
|
||||
)
|
||||
|
||||
case *neutrinorpc.Config:
|
||||
subCfgValue := extractReflectValue(subCfg)
|
||||
|
|
Loading…
Add table
Reference in a new issue