mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
e41c65785f
Move merge of our features into the feature manager, rather than updating our node announcement then retrospectively setting the feature manger's set to the new vector. This allows us to use the feature vector as our single source of truth for announcements.
403 lines
11 KiB
Go
403 lines
11 KiB
Go
//go:build peersrpc
|
|
// +build peersrpc
|
|
|
|
package peersrpc
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"sync/atomic"
|
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
"github.com/lightningnetwork/lnd/feature"
|
|
"github.com/lightningnetwork/lnd/lncfg"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/netann"
|
|
"google.golang.org/grpc"
|
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
|
)
|
|
|
|
const (
|
|
// subServerName is the name of the sub rpc server. We'll use this name
|
|
// to register ourselves, and we also require that the main
|
|
// SubServerConfigDispatcher instance recognize tt as the name of our
|
|
// RPC service.
|
|
subServerName = "PeersRPC"
|
|
)
|
|
|
|
var (
|
|
// macPermissions maps RPC calls to the permissions they require.
|
|
macPermissions = map[string][]bakery.Op{
|
|
"/peersrpc.Peers/UpdateNodeAnnouncement": {{
|
|
Entity: "peers",
|
|
Action: "write",
|
|
}},
|
|
}
|
|
)
|
|
|
|
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
|
// It is used to register the gRPC sub-server with the root server before we
|
|
// have the necessary dependencies to populate the actual sub-server.
|
|
type ServerShell struct {
|
|
PeersServer
|
|
}
|
|
|
|
// Server is a sub-server of the main RPC server: the peers RPC. This sub
|
|
// RPC server allows to intereact with our Peers in the Lightning Network.
|
|
type Server struct {
|
|
started int32 // To be used atomically.
|
|
shutdown int32 // To be used atomically.
|
|
|
|
// Required by the grpc-gateway/v2 library for forward compatibility.
|
|
// Must be after the atomically used variables to not break struct
|
|
// alignment.
|
|
UnimplementedPeersServer
|
|
|
|
cfg *Config
|
|
}
|
|
|
|
// A compile time check to ensure that Server fully implements the PeersServer
|
|
// gRPC service.
|
|
var _ PeersServer = (*Server)(nil)
|
|
|
|
// New returns a new instance of the peersrpc Peers sub-server. We also
|
|
// return the set of permissions for the macaroons that we may create within
|
|
// this method. If the macaroons we need aren't found in the filepath, then
|
|
// we'll create them on start up. If we're unable to locate, or create the
|
|
// macaroons we need, then we'll return with an error.
|
|
func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
|
|
server := &Server{
|
|
cfg: cfg,
|
|
}
|
|
|
|
return server, macPermissions, nil
|
|
}
|
|
|
|
// Start launches any helper goroutines required for the Server to function.
|
|
//
|
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
|
func (s *Server) Start() error {
|
|
if atomic.AddInt32(&s.started, 1) != 1 {
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Stop signals any active goroutines for a graceful closure.
|
|
//
|
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
|
func (s *Server) Stop() error {
|
|
if atomic.AddInt32(&s.shutdown, 1) != 1 {
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Name returns a unique string representation of the sub-server. This can be
|
|
// used to identify the sub-server and also de-duplicate them.
|
|
//
|
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
|
func (s *Server) Name() string {
|
|
return subServerName
|
|
}
|
|
|
|
// RegisterWithRootServer will be called by the root gRPC server to direct a
|
|
// sub RPC server to register itself with the main gRPC root server. Until this
|
|
// is called, each sub-server won't be able to have
|
|
// requests routed towards it.
|
|
//
|
|
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
|
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
|
// We make sure that we register it with the main gRPC server to ensure
|
|
// all our methods are routed properly.
|
|
RegisterPeersServer(grpcServer, r)
|
|
|
|
log.Debugf("Peers RPC server successfully register with root " +
|
|
"gRPC server")
|
|
|
|
return nil
|
|
}
|
|
|
|
// RegisterWithRestServer will be called by the root REST mux to direct a sub
|
|
// RPC server to register itself with the main REST mux server. Until this is
|
|
// called, each sub-server won't be able to have requests routed towards it.
|
|
//
|
|
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
|
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
|
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
|
|
|
// We make sure that we register it with the main REST server to ensure
|
|
// all our methods are routed properly.
|
|
err := RegisterPeersHandlerFromEndpoint(ctx, mux, dest, opts)
|
|
if err != nil {
|
|
log.Errorf("Could not register Peers REST server "+
|
|
"with root REST server: %v", err)
|
|
return err
|
|
}
|
|
|
|
log.Debugf("Peers REST server successfully registered with " +
|
|
"root REST server")
|
|
return nil
|
|
}
|
|
|
|
// CreateSubServer populates the subserver's dependencies using the passed
|
|
// SubServerConfigDispatcher. This method should fully initialize the
|
|
// sub-server instance, making it ready for action. It returns the macaroon
|
|
// permissions that the sub-server wishes to pass on to the root server for all
|
|
// methods routed towards it.
|
|
//
|
|
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
|
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
|
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
|
|
|
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
r.PeersServer = subServer
|
|
return subServer, macPermissions, nil
|
|
}
|
|
|
|
// updateAddresses computes the new address set after executing the update
|
|
// actions.
|
|
func (s *Server) updateAddresses(currentAddresses []net.Addr,
|
|
updates []*UpdateAddressAction) ([]net.Addr, *lnrpc.Op, error) {
|
|
|
|
// net.Addr is not comparable so we cannot use the default map
|
|
// (map[net.Addr]struct{}) so we have to use arrays and a helping
|
|
// function.
|
|
findAddr := func(addr net.Addr, slice []net.Addr) bool {
|
|
for _, sAddr := range slice {
|
|
if sAddr.Network() != addr.Network() {
|
|
continue
|
|
}
|
|
|
|
if sAddr.String() == addr.String() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Preallocate enough memory for both arrays.
|
|
removeAddr := make([]net.Addr, 0, len(updates))
|
|
addAddr := make([]net.Addr, 0, len(updates))
|
|
for _, update := range updates {
|
|
addr, err := s.cfg.ParseAddr(update.Address)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to resolve "+
|
|
"address %v: %v", update.Address, err)
|
|
}
|
|
|
|
switch update.Action {
|
|
case UpdateAction_ADD:
|
|
addAddr = append(addAddr, addr)
|
|
case UpdateAction_REMOVE:
|
|
removeAddr = append(removeAddr, addr)
|
|
default:
|
|
return nil, nil, fmt.Errorf("invalid address update "+
|
|
"action: %v", update.Action)
|
|
}
|
|
}
|
|
|
|
// Look for any inconsistency trying to add AND remove the same address.
|
|
for _, addr := range removeAddr {
|
|
if findAddr(addr, addAddr) {
|
|
return nil, nil, fmt.Errorf("invalid updates for "+
|
|
"removing AND adding %v", addr)
|
|
}
|
|
}
|
|
|
|
ops := &lnrpc.Op{Entity: "addresses"}
|
|
newAddrs := make([]net.Addr, 0, len(updates)+len(currentAddresses))
|
|
|
|
// Copy current addresses excluding the ones that need to be removed.
|
|
for _, addr := range currentAddresses {
|
|
if findAddr(addr, removeAddr) {
|
|
ops.Actions = append(
|
|
ops.Actions,
|
|
fmt.Sprintf("%s removed", addr.String()),
|
|
)
|
|
continue
|
|
}
|
|
newAddrs = append(newAddrs, addr)
|
|
}
|
|
|
|
// Add new adresses if needed.
|
|
for _, addr := range addAddr {
|
|
if !findAddr(addr, newAddrs) {
|
|
ops.Actions = append(
|
|
ops.Actions,
|
|
fmt.Sprintf("%s added", addr.String()),
|
|
)
|
|
newAddrs = append(newAddrs, addr)
|
|
}
|
|
}
|
|
|
|
return newAddrs, ops, nil
|
|
}
|
|
|
|
// updateFeatures computes the new raw SetNodeAnn after executing the update
|
|
// actions.
|
|
func (s *Server) updateFeatures(currentfeatures *lnwire.RawFeatureVector,
|
|
updates []*UpdateFeatureAction) (*lnwire.RawFeatureVector,
|
|
*lnrpc.Op, error) {
|
|
|
|
ops := &lnrpc.Op{Entity: "features"}
|
|
raw := currentfeatures.Clone()
|
|
|
|
for _, update := range updates {
|
|
bit := lnwire.FeatureBit(update.FeatureBit)
|
|
|
|
switch update.Action {
|
|
case UpdateAction_ADD:
|
|
if raw.IsSet(bit) {
|
|
return nil, nil, fmt.Errorf(
|
|
"invalid add action for bit %v, "+
|
|
"bit is already set",
|
|
update.FeatureBit,
|
|
)
|
|
}
|
|
raw.Set(bit)
|
|
ops.Actions = append(
|
|
ops.Actions,
|
|
fmt.Sprintf("%s set", lnwire.Features[bit]),
|
|
)
|
|
|
|
case UpdateAction_REMOVE:
|
|
if !raw.IsSet(bit) {
|
|
return nil, nil, fmt.Errorf(
|
|
"invalid remove action for bit %v, "+
|
|
"bit is already unset",
|
|
update.FeatureBit,
|
|
)
|
|
}
|
|
raw.Unset(bit)
|
|
ops.Actions = append(
|
|
ops.Actions,
|
|
fmt.Sprintf("%s unset", lnwire.Features[bit]),
|
|
)
|
|
|
|
default:
|
|
return nil, nil, fmt.Errorf(
|
|
"invalid update action (%v) for bit %v",
|
|
update.Action,
|
|
update.FeatureBit,
|
|
)
|
|
}
|
|
}
|
|
|
|
// Validate our new SetNodeAnn.
|
|
fv := lnwire.NewFeatureVector(raw, lnwire.Features)
|
|
if err := feature.ValidateDeps(fv); err != nil {
|
|
return nil, nil, fmt.Errorf(
|
|
"invalid feature set (SetNodeAnn): %v",
|
|
err,
|
|
)
|
|
}
|
|
|
|
return raw, ops, nil
|
|
}
|
|
|
|
// UpdateNodeAnnouncement allows the caller to update the node parameters
|
|
// and broadcasts a new version of the node announcement to its peers.
|
|
func (s *Server) UpdateNodeAnnouncement(_ context.Context,
|
|
req *NodeAnnouncementUpdateRequest) (
|
|
*NodeAnnouncementUpdateResponse, error) {
|
|
|
|
resp := &NodeAnnouncementUpdateResponse{}
|
|
nodeModifiers := make([]netann.NodeAnnModifier, 0)
|
|
|
|
currentNodeAnn := s.cfg.GetNodeAnnouncement()
|
|
|
|
nodeAnnFeatures := currentNodeAnn.Features
|
|
featureUpdates := len(req.FeatureUpdates) > 0
|
|
if featureUpdates {
|
|
var (
|
|
ops *lnrpc.Op
|
|
err error
|
|
)
|
|
nodeAnnFeatures, ops, err = s.updateFeatures(
|
|
nodeAnnFeatures, req.FeatureUpdates,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error trying to update node "+
|
|
"features: %w", err)
|
|
}
|
|
resp.Ops = append(resp.Ops, ops)
|
|
}
|
|
|
|
if req.Color != "" {
|
|
color, err := lncfg.ParseHexColor(req.Color)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to parse color: %v", err)
|
|
}
|
|
|
|
if color != currentNodeAnn.RGBColor {
|
|
resp.Ops = append(resp.Ops, &lnrpc.Op{
|
|
Entity: "color",
|
|
Actions: []string{
|
|
fmt.Sprintf("changed to %v", color),
|
|
},
|
|
})
|
|
nodeModifiers = append(
|
|
nodeModifiers,
|
|
netann.NodeAnnSetColor(color),
|
|
)
|
|
}
|
|
}
|
|
|
|
if req.Alias != "" {
|
|
alias, err := lnwire.NewNodeAlias(req.Alias)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid alias value: %v", err)
|
|
}
|
|
if alias != currentNodeAnn.Alias {
|
|
resp.Ops = append(resp.Ops, &lnrpc.Op{
|
|
Entity: "alias",
|
|
Actions: []string{
|
|
fmt.Sprintf("changed to %v", alias),
|
|
},
|
|
})
|
|
nodeModifiers = append(
|
|
nodeModifiers,
|
|
netann.NodeAnnSetAlias(alias),
|
|
)
|
|
}
|
|
}
|
|
|
|
if len(req.AddressUpdates) > 0 {
|
|
newAddrs, ops, err := s.updateAddresses(
|
|
currentNodeAnn.Addresses,
|
|
req.AddressUpdates,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error trying to update node "+
|
|
"addresses: %w", err)
|
|
}
|
|
resp.Ops = append(resp.Ops, ops)
|
|
nodeModifiers = append(
|
|
nodeModifiers,
|
|
netann.NodeAnnSetAddrs(newAddrs),
|
|
)
|
|
}
|
|
|
|
if len(nodeModifiers) == 0 && !featureUpdates {
|
|
return nil, fmt.Errorf("unable to detect any new values to " +
|
|
"update the node announcement")
|
|
}
|
|
|
|
if err := s.cfg.UpdateNodeAnnouncement(
|
|
nodeAnnFeatures, nodeModifiers...,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|