func (n *Node) err(err error) {
	log.Error(errors.FullTrace(err))
}
err handles errors produced by the foreign node.
package wallet
import (
	"crypto/tls"
	"encoding/json"
	"math/rand"
	"net"
	"sync"
	"time"
	"github.com/lbryio/lbry.go/v2/extras/errors"
	"github.com/lbryio/lbry.go/v2/extras/stop"
	log "github.com/sirupsen/logrus"
	"go.uber.org/atomic"
)
const (
	ClientVersion   = "0.0.1"
	ProtocolVersion = "1.0"
)
var (
	ErrNotImplemented = errors.Base("not implemented")
	ErrNodeConnected  = errors.Base("node already connected")
	ErrConnectFailed  = errors.Base("failed to connect")
	ErrTimeout        = errors.Base("timeout")
)
type response struct {
	data []byte
	err  error
}
type Node struct {
	transport *TCPTransport
	nextId    atomic.Uint32
	grp       *stop.Group
	handlersMu *sync.RWMutex
	handlers   map[uint32]chan response
	pushHandlersMu *sync.RWMutex
	pushHandlers   map[string][]chan response
	timeout time.Duration
}
func NewNode() *Node {
	return &Node{
		handlers:       make(map[uint32]chan response),
		pushHandlers:   make(map[string][]chan response),
		handlersMu:     &sync.RWMutex{},
		pushHandlersMu: &sync.RWMutex{},
		grp:            stop.New(),
		timeout:        1 * time.Second,
	}
}
func (n *Node) Connect(addrs []string, config *tls.Config) error {
	if n.transport != nil {
		return errors.Err(ErrNodeConnected)
	}
	rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] })
	var err error
	for _, addr := range addrs {
		n.transport, err = NewTransport(addr, config)
		if err == nil {
			break
		}
		if errors.Is(err, ErrTimeout) {
			continue
		}
		if e, ok := err.(*net.OpError); ok && e.Err.Error() == "no such host" {
			continue
		}
		return errors.Err(err)
	}
	if n.transport == nil {
		return errors.Err(ErrConnectFailed)
	}
	log.Debugf("wallet connected to %s", n.transport.conn.RemoteAddr())
	n.grp.Add(1)
	go func() {
		defer n.grp.Done()
		<-n.grp.Ch()
		n.transport.Shutdown()
	}()
	n.grp.Add(1)
	go func() {
		defer n.grp.Done()
		n.handleErrors()
	}()
	n.grp.Add(1)
	go func() {
		defer n.grp.Done()
		n.listen()
	}()
	return nil
}
func (n *Node) Shutdown() {
	var addr net.Addr
	if n.transport != nil {
		addr = n.transport.conn.RemoteAddr()
	}
	log.Debugf("shutting down wallet %s", addr)
	n.grp.StopAndWait()
	log.Debugf("wallet stopped")
}
func (n *Node) handleErrors() {
	for {
		select {
		case <-n.grp.Ch():
			return
		case err := <-n.transport.Errors():
			n.err(errors.Err(err))
		}
	}
}