func GetCurrentBP() (bpNodeID proto.NodeID, err error) {
	currentBPLock.Lock()
	defer currentBPLock.Unlock()
	if !currentBP.IsEmpty() {
		bpNodeID = currentBP
		return
	}
	var localNodeID proto.NodeID
	if localNodeID, err = kms.GetLocalNodeID(); err != nil {
		return
	}
	bpList := route.GetBPs()
	if len(bpList) == 0 {
		err = ErrNoChiefBlockProducerAvailable
		return
	}
	randomBP := bpList[rand.Intn(len(bpList))]
	req := &proto.FindNeighborReq{
		ID: localNodeID,
		Roles: []proto.ServerRole{
			proto.Leader,
			proto.Follower,
		},
		Count: 1,
	}
	res := new(proto.FindNeighborResp)
	if err = NewCaller().CallNode(randomBP, "DHT.FindNeighbor", req, res); err != nil {
		return
	}
	if len(res.Nodes) <= 0 {
		err = errors.Wrap(ErrNoChiefBlockProducerAvailable,
			"get no hash nearest block producer nodes")
		return
	}
	if res.Nodes[0].Role != proto.Leader && res.Nodes[0].Role != proto.Follower {
		err = errors.Wrap(ErrNoChiefBlockProducerAvailable,
			"no suitable nodes with proper block producer role")
		return
	}
	currentBP = res.Nodes[0].ID
	bpNodeID = currentBP
	return
}
GetCurrentBP returns nearest hash distance block producer as current node chief block producer.
package mux
import (
	"math/rand"
	"sync"
	"time"
	"github.com/pkg/errors"
	"github.com/SQLess/SQLess/crypto/kms"
	"github.com/SQLess/SQLess/naconn"
	"github.com/SQLess/SQLess/proto"
	"github.com/SQLess/SQLess/route"
	"github.com/SQLess/SQLess/utils/log"
)
var (
	ErrNoChiefBlockProducerAvailable = errors.New("no chief block producer found")
	currentBP proto.NodeID
	currentBPLock sync.Mutex
)
type Resolver struct {
	direct bool
}
func NewResolver() naconn.Resolver {
	return &Resolver{}
}
func NewDirectResolver() naconn.Resolver {
	return &Resolver{direct: true}
}
func (r *Resolver) Resolve(id *proto.RawNodeID) (string, error) {
	if r.direct {
		node, err := GetNodeInfo(id)
		if err != nil {
			return "", err
		}
		if node.Role == proto.Miner {
			return node.DirectAddr, nil
		}
		return node.Addr, nil
	}
	return GetNodeAddr(id)
}
func (r *Resolver) ResolveEx(id *proto.RawNodeID) (*proto.Node, error) {
	return GetNodeInfo(id)
}
func init() {
	naconn.RegisterResolver(&Resolver{})
}
func GetNodeAddr(id *proto.RawNodeID) (addr string, err error) {
	addr, err = route.GetNodeAddrCache(id)
	if err != nil {
		if err == route.ErrUnknownNodeID {
			var node *proto.Node
			node, err = FindNodeInBP(id)
			if err != nil {
				return
			}
			_ = route.SetNodeAddrCache(id, node.Addr)
			addr = node.Addr
		}
	}
	return
}
func GetNodeInfo(id *proto.RawNodeID) (nodeInfo *proto.Node, err error) {
	nodeInfo, err = kms.GetNodeInfo(proto.NodeID(id.String()))
	if err != nil {
		if errors.Cause(err) == kms.ErrKeyNotFound {
			nodeInfo, err = FindNodeInBP(id)
			if err != nil {
				return
			}
			errSet := route.SetNodeAddrCache(id, nodeInfo.Addr)
			if errSet != nil {
				log.WithError(errSet).Warning("set node addr cache failed")
			}
			errSet = kms.SetNode(nodeInfo)
			if errSet != nil {
				log.WithError(errSet).Warning("set node to kms failed")
			}
		}
	}
	return
}
func FindNodeInBP(id *proto.RawNodeID) (node *proto.Node, err error) {
	bps := route.GetBPs()
	if len(bps) == 0 {
		err = errors.New("no available BP")
		return
	}
	client := NewCaller()
	req := &proto.FindNodeReq{
		ID: proto.NodeID(id.String()),
	}
	resp := new(proto.FindNodeResp)
	bpCount := len(bps)
	offset := rand.Intn(bpCount)
	method := route.DHTFindNode.String()
	for i := 0; i != bpCount; i++ {
		bp := bps[(offset+i)%bpCount]
		err = client.CallNode(bp, method, req, resp)
		if err == nil {
			node = resp.Node
			return
		}
		log.WithFields(log.Fields{
			"method": method,
			"bp":     bp,
		}).WithError(err).Warning("call dht rpc failed")
	}
	err = errors.Wrapf(err, "could not find node in all block producers")
	return
}
func PingBP(node *proto.Node, BPNodeID proto.NodeID) (err error) {
	client := NewCaller()
	req := &proto.PingReq{
		Node: *node,
	}
	resp := new(proto.PingResp)
	err = client.CallNode(BPNodeID, "DHT.Ping", req, resp)
	if err != nil {
		err = errors.Wrap(err, "call DHT.Ping failed")
		return
	}
	return
}