diff --git a/addrmgr/addrmanager.go b/addrmgr/addrmanager.go index 6fbcc09f..47585d55 100644 --- a/addrmgr/addrmanager.go +++ b/addrmgr/addrmanager.go @@ -45,7 +45,7 @@ type AddrManager struct { nTried int nNew int lamtx sync.Mutex - localAddresses map[string]*localAddress + localAddresses map[string]*LocalAddress version int } @@ -69,9 +69,9 @@ type serializedAddrManager struct { TriedBuckets [triedBucketCount][]string } -type localAddress struct { - na *wire.NetAddress - score AddressPriority +type LocalAddress struct { + NA *wire.NetAddress + Score AddressPriority } // AddressPriority type is used to describe the hierarchy of local address @@ -178,7 +178,7 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) { // note that to prevent causing excess garbage on getaddr // messages the netaddresses in addrmaanger are *immutable*, // if we need to change them then we replace the pointer with a - // new copy so that we don't have to copy every na for getaddr. + // new copy so that we don't have to copy every NA for getaddr. if netAddr.Timestamp.After(ka.na.Timestamp) || (ka.na.Services&netAddr.Services) != netAddr.Services { @@ -753,7 +753,7 @@ func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.S // the relevant .onion address. func ipString(na *wire.NetAddress) string { if IsOnionCatTor(na) { - // We know now that na.IP is long enough. + // We know now that NA.IP is long enough. base32 := base32.StdEncoding.EncodeToString(na.IP[6:]) return strings.ToLower(base32) + ".onion" } @@ -877,7 +877,7 @@ func (a *AddrManager) Connected(addr *wire.NetAddress) { // so. now := time.Now() if now.After(ka.na.Timestamp.Add(time.Minute * 20)) { - // ka.na is immutable, so replace it. + // ka.NA is immutable, so replace it. naCopy := *ka.na naCopy.Timestamp = time.Now() ka.na = &naCopy @@ -985,14 +985,14 @@ func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFl // Update the services if needed. if ka.na.Services != services { - // ka.na is immutable, so replace it. + // ka.NA is immutable, so replace it. naCopy := *ka.na naCopy.Services = services ka.na = &naCopy } } -// AddLocalAddress adds na to the list of known local addresses to advertise +// AddLocalAddress adds NA to the list of known local addresses to advertise // with the given priority. func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPriority) error { if !IsRoutable(na) { @@ -1004,13 +1004,13 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior key := NetAddressKey(na) la, ok := a.localAddresses[key] - if !ok || la.score < priority { + if !ok || la.Score < priority { if ok { - la.score = priority + 1 + la.Score = priority + 1 } else { - a.localAddresses[key] = &localAddress{ - na: na, - score: priority, + a.localAddresses[key] = &LocalAddress{ + NA: na, + Score: priority, } } } @@ -1106,12 +1106,12 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net var bestscore AddressPriority var bestAddress *wire.NetAddress for _, la := range a.localAddresses { - reach := getReachabilityFrom(la.na, remoteAddr) + reach := getReachabilityFrom(la.NA, remoteAddr) if reach > bestreach || - (reach == bestreach && la.score > bestscore) { + (reach == bestreach && la.Score > bestscore) { bestreach = reach - bestscore = la.score - bestAddress = la.na + bestscore = la.Score + bestAddress = la.NA } } if bestAddress != nil { @@ -1135,6 +1135,15 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net return bestAddress } +// LocalAddresses returns the list of local addresses for our node. +func (a *AddrManager) LocalAddresses() []*LocalAddress { + var addrs []*LocalAddress + for _, addr := range a.localAddresses { + addrs = append(addrs, addr) + } + return addrs +} + // New returns a new bitcoin address manager. // Use Start to begin processing asynchronous address updates. func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager { @@ -1143,7 +1152,7 @@ func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager lookupFunc: lookupFunc, rand: rand.New(rand.NewSource(time.Now().UnixNano())), quit: make(chan struct{}), - localAddresses: make(map[string]*localAddress), + localAddresses: make(map[string]*LocalAddress), version: serialisationVersion, } am.reset() diff --git a/blockchain/chain.go b/blockchain/chain.go index a0bfac6c..66c435d8 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -199,6 +199,15 @@ func (b *BlockChain) HaveBlock(hash *chainhash.Hash) (bool, error) { return exists || b.IsKnownOrphan(hash), nil } +// GetWarnings returns a bool for whether unknownRules +// has been warned. +func (b *BlockChain) GetWarnings() bool { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + return b.unknownRulesWarned +} + // IsKnownOrphan returns whether the passed hash is currently a known orphan. // Keep in mind that only a limited number of orphans are held onto for a // limited amount of time, so this function must not be used as an absolute diff --git a/rpcserver.go b/rpcserver.go index 3c0530fe..029fdc4b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -28,6 +28,7 @@ import ( "time" "github.com/btcsuite/websocket" + "github.com/lbryio/lbcd/addrmgr" "github.com/lbryio/lbcd/blockchain" "github.com/lbryio/lbcd/blockchain/indexers" "github.com/lbryio/lbcd/btcec" @@ -160,6 +161,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{ "getmininginfo": handleGetMiningInfo, "getnettotals": handleGetNetTotals, "getnetworkhashps": handleGetNetworkHashPS, + "getnetworkinfo": handleGetNetworkInfo, "getnodeaddresses": handleGetNodeAddresses, "getpeerinfo": handleGetPeerInfo, "getrawmempool": handleGetRawMempool, @@ -234,7 +236,6 @@ var rpcUnimplemented = map[string]struct{}{ "estimatepriority": {}, "getchaintips": {}, "getmempoolentry": {}, - "getnetworkinfo": {}, "getwork": {}, "invalidateblock": {}, "preciousblock": {}, @@ -2603,6 +2604,84 @@ func handleGetNodeAddresses(s *rpcServer, cmd interface{}, closeChan <-chan stru return addresses, nil } +// handleGetNetworkInfo implements the getnetworkinfo command. +func handleGetNetworkInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + ver := wire.MsgVersion{} + _ = ver.AddUserAgent(userAgentName, userAgentVersion, cfg.UserAgentComments...) + + var localAddrs []btcjson.LocalAddressesResult + var ipv4Reachable, ipv6Reachable bool + for _, addr := range s.cfg.AddrMgr.LocalAddresses() { + localAddrs = append(localAddrs, btcjson.LocalAddressesResult{ + Address: addr.NA.IP.String(), + Port: addr.NA.Port, + Score: int32(addr.Score), + }) + if addr.NA.IP.To4() != nil { + ipv4Reachable = true + } else { + ipv6Reachable = true + } + } + + onionProxy := cfg.Proxy + if cfg.OnionProxy != "" { + onionProxy = cfg.OnionProxy + } + + var warnings string + unknownRulesWarned := s.cfg.Chain.GetWarnings() + if unknownRulesWarned { + warnings = "Warning: Unknown new rules activated! " + } + + var timeOffset int64 + if !s.cfg.SyncMgr.IsCurrent() { + ss := s.cfg.Chain.BestSnapshot() + bestHeader, err := s.cfg.Chain.HeaderByHash(&ss.Hash) + if err != nil { + return nil, err + } + timeOffset = int64(time.Since(bestHeader.Timestamp).Seconds()) + } + + reply := &btcjson.GetNetworkInfoResult{ + ProtocolVersion: int32(wire.ProtocolVersion), + Version: versionNumeric(), + Connections: s.cfg.ConnMgr.ConnectedCount(), + IncrementalFee: cfg.MinRelayTxFee, + LocalAddresses: localAddrs, + LocalRelay: !cfg.BlocksOnly, + LocalServices: s.cfg.Services.String(), + NetworkActive: true, + Networks: []btcjson.NetworksResult{ + { + Name: "ipv4", + Reachable: ipv4Reachable, + Proxy: cfg.Proxy, + }, + { + Name: "ipv6", + Reachable: ipv6Reachable, + Proxy: cfg.Proxy, + }, + { + Name: "onion", + + ProxyRandomizeCredentials: cfg.TorIsolation, + + Proxy: onionProxy, + Reachable: cfg.Proxy != "" || cfg.OnionProxy != "", + }, + }, + RelayFee: cfg.MinRelayTxFee, + SubVersion: ver.UserAgent, + TimeOffset: timeOffset, + Warnings: warnings, + } + return reply, nil +} + // handleGetPeerInfo implements the getpeerinfo command. func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { peers := s.cfg.ConnMgr.ConnectedPeers() @@ -4662,6 +4741,9 @@ type rpcserverConfig struct { // connection-related data and tasks. ConnMgr rpcserverConnManager + // AddrMgr is the server's instance of the AddressManager. + AddrMgr *addrmgr.AddrManager + // SyncMgr defines the sync manager for the RPC server to use. SyncMgr rpcserverSyncManager @@ -4692,6 +4774,9 @@ type rpcserverConfig struct { // The fee estimator keeps track of how long transactions are left in // the mempool before they are mined into blocks. FeeEstimator *mempool.FeeEstimator + + // Services represents the services supported by this node. + Services wire.ServiceFlag } // newRPCServer returns a new instance of the rpcServer struct. diff --git a/rpcserverhelp.go b/rpcserverhelp.go index 12bef63d..ebeffcd8 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -454,6 +454,27 @@ var helpDescsEnUS = map[string]string{ "getnetworkhashps-height": "Perform estimate ending with this height or -1 for current best chain block height", "getnetworkhashps--result0": "Estimated hashes per second", + // GetNetworkInfo help. + "getnetworkinfo--synopsis": "Returns an object containing various state info regarding P2P networking.", + "getnetworkinfo--result0--desc": "GetNetworkInfo object", + "getnetworkinfo--result0--key": "Field name", + "getnetworkinfo--result0--value": "Object containing the network info", + + // GetNetworkInfoResult help. + "getnetworkinforesult-version": "The server version", + "getnetworkinforesult-subversion": "The server subversion string", + "getnetworkinforesult-protocolversion": "The protocol version", + "getnetworkinforesult-localservices": "The services we offer to the network", + "getnetworkinforesult-localrelay": "True if transaction relay is requested from peers", + "getnetworkinforesult-timeoffset": "The time offset", + "getnetworkinforesult-connections": "The number of connections", + "getnetworkinforesult-networkactive": "Whether p2p networking is enabled", + "getnetworkinforesult-networks": "Information per network", + "getnetworkinforesult-relayfee": "Minimum relay fee for transactions in BTC/kB", + "getnetworkinforesult-incrementalfee": "Minimum fee increment for mempool limiting or BIP 125 replacement in BTC/kB", + "getnetworkinforesult-localaddresses": "List of local addresses", + "getnetworkinforesult-warnings": "Any network and blockchain warnings", + // GetNetTotalsCmd help. "getnettotals--synopsis": "Returns a JSON object containing network traffic statistics.", @@ -819,6 +840,7 @@ var rpcResultTypes = map[string][]interface{}{ "getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)}, "getnettotals": {(*btcjson.GetNetTotalsResult)(nil)}, "getnetworkhashps": {(*int64)(nil)}, + "getnetworkinfo": {(*map[string]btcjson.GetNetworkInfoResult)(nil)}, "getnodeaddresses": {(*[]btcjson.GetNodeAddressesResult)(nil)}, "getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)}, "getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)}, diff --git a/server.go b/server.go index bdec05e9..ec492431 100644 --- a/server.go +++ b/server.go @@ -2975,6 +2975,7 @@ func newServer(listenAddrs, agentBlacklist, agentWhitelist []string, Listeners: rpcListeners, StartupTime: startupTime.Unix(), ConnMgr: &rpcConnManager{&s}, + AddrMgr: amgr, SyncMgr: &rpcSyncMgr{&s, s.syncManager}, TimeSource: s.timeSource, Chain: s.chain, @@ -2987,6 +2988,7 @@ func newServer(listenAddrs, agentBlacklist, agentWhitelist []string, AddrIndex: s.addrIndex, CfIndex: s.cfIndex, FeeEstimator: s.feeEstimator, + Services: s.services, }) if err != nil { return nil, err diff --git a/version.go b/version.go index 5c8cc309..89c3ff19 100644 --- a/version.go +++ b/version.go @@ -57,6 +57,11 @@ func version() string { return version } +// Numeric returns the application version as an integer. +func versionNumeric() int32 { + return int32(2 ^ appMajor*3 ^ appMinor*5 ^ appPatch) +} + // normalizeVerString returns the passed string stripped of all characters which // are not valid according to the semantic versioning guidelines for pre-release // version and build metadata strings. In particular they MUST only contain