github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/config/application_config.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
    10  )
    11  
    12  // ApplicationConfiguration config specific to the node.
    13  type ApplicationConfiguration struct {
    14  	Ledger `yaml:",inline"`
    15  
    16  	DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"`
    17  
    18  	LogLevel string `yaml:"LogLevel"`
    19  	LogPath  string `yaml:"LogPath"`
    20  
    21  	P2P P2P `yaml:"P2P"`
    22  
    23  	Pprof      BasicService `yaml:"Pprof"`
    24  	Prometheus BasicService `yaml:"Prometheus"`
    25  
    26  	Relay     bool                `yaml:"Relay"`
    27  	Consensus Consensus           `yaml:"Consensus"`
    28  	RPC       RPC                 `yaml:"RPC"`
    29  	Oracle    OracleConfiguration `yaml:"Oracle"`
    30  	P2PNotary P2PNotary           `yaml:"P2PNotary"`
    31  	StateRoot StateRoot           `yaml:"StateRoot"`
    32  }
    33  
    34  // EqualsButServices returns true when the o is the same as a except for services
    35  // (Oracle, P2PNotary, Pprof, Prometheus, RPC and StateRoot sections)
    36  // and LogLevel field.
    37  func (a *ApplicationConfiguration) EqualsButServices(o *ApplicationConfiguration) bool {
    38  	if len(a.P2P.Addresses) != len(o.P2P.Addresses) {
    39  		return false
    40  	}
    41  	aCp := make([]string, len(a.P2P.Addresses))
    42  	oCp := make([]string, len(o.P2P.Addresses))
    43  	copy(aCp, a.P2P.Addresses)
    44  	copy(oCp, o.P2P.Addresses)
    45  	sort.Strings(aCp)
    46  	sort.Strings(oCp)
    47  	for i := range aCp {
    48  		if aCp[i] != oCp[i] {
    49  			return false
    50  		}
    51  	}
    52  	if a.P2P.AttemptConnPeers != o.P2P.AttemptConnPeers ||
    53  		a.P2P.BroadcastFactor != o.P2P.BroadcastFactor ||
    54  		a.DBConfiguration != o.DBConfiguration ||
    55  		a.P2P.DialTimeout != o.P2P.DialTimeout ||
    56  		a.P2P.ExtensiblePoolSize != o.P2P.ExtensiblePoolSize ||
    57  		a.LogPath != o.LogPath ||
    58  		a.P2P.MaxPeers != o.P2P.MaxPeers ||
    59  		a.P2P.MinPeers != o.P2P.MinPeers ||
    60  		a.P2P.PingInterval != o.P2P.PingInterval ||
    61  		a.P2P.PingTimeout != o.P2P.PingTimeout ||
    62  		a.P2P.ProtoTickInterval != o.P2P.ProtoTickInterval ||
    63  		a.Relay != o.Relay {
    64  		return false
    65  	}
    66  	return true
    67  }
    68  
    69  // AnnounceableAddress is a pair of node address in the form of "[host]:[port]"
    70  // with optional corresponding announced port to be used in version exchange.
    71  type AnnounceableAddress struct {
    72  	Address       string
    73  	AnnouncedPort uint16
    74  }
    75  
    76  // GetAddresses parses returns the list of AnnounceableAddress containing information
    77  // gathered from Addresses.
    78  func (a *ApplicationConfiguration) GetAddresses() ([]AnnounceableAddress, error) {
    79  	addrs := make([]AnnounceableAddress, 0, len(a.P2P.Addresses))
    80  	for i, addrStr := range a.P2P.Addresses {
    81  		if len(addrStr) == 0 {
    82  			return nil, fmt.Errorf("address #%d is empty", i)
    83  		}
    84  		lastCln := strings.LastIndex(addrStr, ":")
    85  		if lastCln == -1 {
    86  			addrs = append(addrs, AnnounceableAddress{
    87  				Address: addrStr, // Plain IPv4 address without port.
    88  			})
    89  			continue
    90  		}
    91  		lastPort, err := strconv.ParseUint(addrStr[lastCln+1:], 10, 16)
    92  		if err != nil {
    93  			addrs = append(addrs, AnnounceableAddress{
    94  				Address: addrStr, // Still may be a valid IPv4 of the form "X.Y.Z.Q:" or plain IPv6 "A:B::", keep it.
    95  			})
    96  			continue
    97  		}
    98  		penultimateCln := strings.LastIndex(addrStr[:lastCln], ":")
    99  		if penultimateCln == -1 {
   100  			addrs = append(addrs, AnnounceableAddress{
   101  				Address: addrStr, // IPv4 address with port "X.Y.Z.Q:123"
   102  			})
   103  			continue
   104  		}
   105  		isV6 := strings.Count(addrStr, ":") > 2
   106  		hasBracket := strings.Contains(addrStr, "]")
   107  		if penultimateCln == lastCln-1 {
   108  			if isV6 && !hasBracket {
   109  				addrs = append(addrs, AnnounceableAddress{
   110  					Address: addrStr, // Plain IPv6 of the form "A:B::123"
   111  				})
   112  			} else {
   113  				addrs = append(addrs, AnnounceableAddress{
   114  					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".
   115  					AnnouncedPort: uint16(lastPort),
   116  				})
   117  			}
   118  			continue
   119  		}
   120  		_, err = strconv.ParseUint(addrStr[penultimateCln+1:lastCln], 10, 16)
   121  		if err != nil {
   122  			if isV6 {
   123  				addrs = append(addrs, AnnounceableAddress{
   124  					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.
   125  				})
   126  				continue
   127  			}
   128  			return nil, fmt.Errorf("failed to parse port from %s: %w", addrStr, err) // Some garbage.
   129  		}
   130  		if isV6 && !hasBracket {
   131  			addrs = append(addrs, AnnounceableAddress{
   132  				Address: addrStr, // Plain IPv6 of the form "A::1:1"
   133  			})
   134  		} else {
   135  			addrs = append(addrs, AnnounceableAddress{
   136  				Address:       addrStr[:lastCln], // IPv4 with both ports or IPv6 with both ports specified.
   137  				AnnouncedPort: uint16(lastPort),
   138  			})
   139  		}
   140  	}
   141  	if len(addrs) == 0 {
   142  		addrs = append(addrs, AnnounceableAddress{
   143  			Address: ":0",
   144  		})
   145  	}
   146  	return addrs, nil
   147  }