package settings import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "strconv" "strings" "time" "github.com/multiformats/go-multiaddr" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-node/pkg/core/netmap" "github.com/nspcc-dev/neofs-node/pkg/network/peers" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" ) type ( nodeSettings struct { dig.Out Address multiaddr.Multiaddr PrivateKey *ecdsa.PrivateKey NodeOpts []string `name:"node_options"` ShutdownTTL time.Duration `name:"shutdown_ttl"` NodeInfo netmap.Info } ) const generateKey = "generated" var errEmptyNodeSettings = errors.New("node settings could not be empty") func newNodeSettings(v *viper.Viper, l *zap.Logger) (cfg nodeSettings, err error) { // check, that we have node settings in provided config if !v.IsSet("node") { err = errEmptyNodeSettings return } // try to load and setup ecdsa.PrivateKey key := v.GetString("node.private_key") switch key { case "": err = crypto.ErrEmptyPrivateKey return cfg, err case generateKey: if cfg.PrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { return cfg, err } default: if cfg.PrivateKey, err = crypto.LoadPrivateKey(key); err != nil { return cfg, errors.Wrap(err, "cannot unmarshal private key") } } id := peers.IDFromPublicKey(&cfg.PrivateKey.PublicKey) pub := crypto.MarshalPublicKey(&cfg.PrivateKey.PublicKey) l.Debug("private key loaded successful", zap.String("file", v.GetString("node.private_key")), zap.Binary("public", pub), zap.Stringer("node-id", id)) var ( addr string proto string ) // fetch shutdown timeout from settings if cfg.ShutdownTTL = v.GetDuration("node.shutdown_ttl"); cfg.ShutdownTTL == 0 { return cfg, errEmptyShutdownTTL } // fetch address and protocol from settings if addr = v.GetString("node.address"); addr == "" { return cfg, errors.Wrapf(errEmptyAddress, "given '%s'", addr) } else if addr, err := prepareAddress(addr); err != nil { return cfg, err } else if proto = v.GetString("node.proto"); proto == "" { return cfg, errors.Wrapf(errEmptyProtocol, "given '%s'", proto) } else if cfg.Address, err = multiAddressFromProtoAddress(proto, addr); err != nil { return cfg, errors.Wrapf(err, "given '%s' '%s'", proto, addr) } // add well-known options items := map[string]string{ "Capacity": "capacity", "Price": "price", "Location": "location", "Country": "country", "City": "city", } // TODO: use const namings prefix := "node." for opt, path := range items { val := v.GetString(prefix + path) if len(val) == 0 { err = errors.Errorf("node option %s must be set explicitly", opt) return } cfg.NodeOpts = append(cfg.NodeOpts, fmt.Sprintf("/%s:%s", opt, val, ), ) } // add other options var ( i int val string ) loop: for ; ; i++ { val = v.GetString("node.options." + strconv.Itoa(i)) if val == "" { break } for opt := range items { if strings.Contains(val, "/"+opt) { continue loop } } cfg.NodeOpts = append(cfg.NodeOpts, val) } nodeInfo := netmap.Info{} nodeInfo.SetAddress(cfg.Address.String()) nodeInfo.SetPublicKey(crypto.MarshalPublicKey(&cfg.PrivateKey.PublicKey)) nodeInfo.SetOptions(cfg.NodeOpts) cfg.NodeInfo = nodeInfo l.Debug("loaded node options", zap.Strings("options", cfg.NodeOpts)) return cfg, err }