neo-go/pkg/config/application_config.go
Ekaterina Pavlova 0b31a29f39 services: add new service for fetching blocks from NeoFS
Close #3496

Co-authored-by: Anna Shaleva <shaleva.ann@nspcc.ru>
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-11 08:50:31 +04:00

154 lines
5 KiB
Go

package config
import (
"fmt"
"slices"
"strconv"
"strings"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
)
// ApplicationConfiguration config specific to the node.
type ApplicationConfiguration struct {
Ledger `yaml:",inline"`
DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"`
LogLevel string `yaml:"LogLevel"`
LogPath string `yaml:"LogPath"`
P2P P2P `yaml:"P2P"`
Pprof BasicService `yaml:"Pprof"`
Prometheus BasicService `yaml:"Prometheus"`
Relay bool `yaml:"Relay"`
Consensus Consensus `yaml:"Consensus"`
RPC RPC `yaml:"RPC"`
Oracle OracleConfiguration `yaml:"Oracle"`
P2PNotary P2PNotary `yaml:"P2PNotary"`
StateRoot StateRoot `yaml:"StateRoot"`
NeoFSBlockFetcher NeoFSBlockFetcher `yaml:"NeoFSBlockFetcher"`
}
// EqualsButServices returns true when the o is the same as a except for services
// (Oracle, P2PNotary, Pprof, Prometheus, RPC and StateRoot sections)
// and LogLevel field.
func (a *ApplicationConfiguration) EqualsButServices(o *ApplicationConfiguration) bool {
if len(a.P2P.Addresses) != len(o.P2P.Addresses) {
return false
}
aCp := slices.Clone(a.P2P.Addresses)
oCp := slices.Clone(o.P2P.Addresses)
slices.Sort(aCp)
slices.Sort(oCp)
if !slices.Equal(aCp, oCp) {
return false
}
if a.P2P.AttemptConnPeers != o.P2P.AttemptConnPeers ||
a.P2P.BroadcastFactor != o.P2P.BroadcastFactor ||
a.DBConfiguration != o.DBConfiguration ||
a.P2P.DialTimeout != o.P2P.DialTimeout ||
a.P2P.ExtensiblePoolSize != o.P2P.ExtensiblePoolSize ||
a.LogPath != o.LogPath ||
a.P2P.MaxPeers != o.P2P.MaxPeers ||
a.P2P.MinPeers != o.P2P.MinPeers ||
a.P2P.PingInterval != o.P2P.PingInterval ||
a.P2P.PingTimeout != o.P2P.PingTimeout ||
a.P2P.ProtoTickInterval != o.P2P.ProtoTickInterval ||
a.Relay != o.Relay {
return false
}
return true
}
// AnnounceableAddress is a pair of node address in the form of "[host]:[port]"
// with optional corresponding announced port to be used in version exchange.
type AnnounceableAddress struct {
Address string
AnnouncedPort uint16
}
// GetAddresses parses returns the list of AnnounceableAddress containing information
// gathered from Addresses.
func (a *ApplicationConfiguration) GetAddresses() ([]AnnounceableAddress, error) {
addrs := make([]AnnounceableAddress, 0, len(a.P2P.Addresses))
for i, addrStr := range a.P2P.Addresses {
if len(addrStr) == 0 {
return nil, fmt.Errorf("address #%d is empty", i)
}
lastCln := strings.LastIndex(addrStr, ":")
if lastCln == -1 {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // Plain IPv4 address without port.
})
continue
}
lastPort, err := strconv.ParseUint(addrStr[lastCln+1:], 10, 16)
if err != nil {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // Still may be a valid IPv4 of the form "X.Y.Z.Q:" or plain IPv6 "A:B::", keep it.
})
continue
}
penultimateCln := strings.LastIndex(addrStr[:lastCln], ":")
if penultimateCln == -1 {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // IPv4 address with port "X.Y.Z.Q:123"
})
continue
}
isV6 := strings.Count(addrStr, ":") > 2
hasBracket := strings.Contains(addrStr, "]")
if penultimateCln == lastCln-1 {
if isV6 && !hasBracket {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // Plain IPv6 of the form "A:B::123"
})
} else {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr[:lastCln], // IPv4 with empty port and non-empty announced port "X.Y.Z.Q::123" or IPv6 with non-empty announced port "[A:B::]::123".
AnnouncedPort: uint16(lastPort),
})
}
continue
}
_, err = strconv.ParseUint(addrStr[penultimateCln+1:lastCln], 10, 16)
if err != nil {
if isV6 {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // Still may be a valid plain IPv6 of the form "A::B:123" or IPv6 with single port [A:B::]:123, keep it.
})
continue
}
return nil, fmt.Errorf("failed to parse port from %s: %w", addrStr, err) // Some garbage.
}
if isV6 && !hasBracket {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr, // Plain IPv6 of the form "A::1:1"
})
} else {
addrs = append(addrs, AnnounceableAddress{
Address: addrStr[:lastCln], // IPv4 with both ports or IPv6 with both ports specified.
AnnouncedPort: uint16(lastPort),
})
}
}
if len(addrs) == 0 {
addrs = append(addrs, AnnounceableAddress{
Address: ":0",
})
}
return addrs, nil
}
// Validate checks ApplicationConfiguration for internal consistency and returns
// an error if any invalid settings are found. This ensures that the application
// configuration is valid and safe to use for further operations.
func (a *ApplicationConfiguration) Validate() error {
if err := a.NeoFSBlockFetcher.Validate(); err != nil {
return fmt.Errorf("invalid NeoFSBlockFetcher config: %w", err)
}
return nil
}