github.com/aergoio/aergo@v1.3.1/polaris/client/polarisconnect.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package client
     7  
     8  import (
     9  	"bufio"
    10  	"errors"
    11  	"fmt"
    12  	"github.com/aergoio/aergo/p2p/p2pkey"
    13  	"github.com/aergoio/aergo/p2p/v030"
    14  	"github.com/aergoio/aergo/polaris/common"
    15  	"github.com/libp2p/go-libp2p-core/network"
    16  	"sync"
    17  
    18  	"github.com/aergoio/aergo-actor/actor"
    19  	"github.com/aergoio/aergo-lib/log"
    20  	"github.com/aergoio/aergo/config"
    21  	"github.com/aergoio/aergo/message"
    22  	"github.com/aergoio/aergo/p2p/p2pcommon"
    23  	"github.com/aergoio/aergo/p2p/p2putil"
    24  	"github.com/aergoio/aergo/pkg/component"
    25  	"github.com/aergoio/aergo/types"
    26  )
    27  
    28  var ErrTooLowVersion = errors.New("aergosvr version is too low")
    29  
    30  // PeerMapService is
    31  type PolarisConnectSvc struct {
    32  	*component.BaseComponent
    33  
    34  	PrivateChain bool
    35  
    36  	mapServers []p2pcommon.PeerMeta
    37  	exposeself bool
    38  
    39  	ntc p2pcommon.NTContainer
    40  	nt  p2pcommon.NetworkTransport
    41  
    42  	rwmutex *sync.RWMutex
    43  }
    44  
    45  func NewPolarisConnectSvc(cfg *config.P2PConfig, ntc p2pcommon.NTContainer) *PolarisConnectSvc {
    46  	pcs := &PolarisConnectSvc{
    47  		rwmutex:    &sync.RWMutex{},
    48  		exposeself: cfg.NPExposeSelf,
    49  	}
    50  	pcs.BaseComponent = component.NewBaseComponent(message.MapSvc, pcs, log.NewLogger("pcs"))
    51  
    52  	// init
    53  	pcs.ntc = ntc
    54  	pcs.initSvc(cfg)
    55  	// TODO need more pretty way to get chainID
    56  
    57  	return pcs
    58  }
    59  
    60  func (pcs *PolarisConnectSvc) initSvc(cfg *config.P2PConfig) {
    61  	pcs.PrivateChain = !pcs.ntc.ChainID().PublicNet
    62  	if cfg.NPUsePolaris {
    63  		// private network does not use public polaris
    64  		if !pcs.PrivateChain {
    65  			servers := make([]string, 0)
    66  			// add hardcoded built-in servers if net is ONE net.
    67  			if *pcs.ntc.ChainID() == common.ONEMainNet {
    68  				pcs.Logger.Info().Msg("chain is ONE Mainnet so use default polaris for mainnet")
    69  				servers = common.MainnetMapServer
    70  			} else if *pcs.ntc.ChainID() == common.ONETestNet {
    71  				pcs.Logger.Info().Msg("chain is ONE Testnet so use default polaris for testnet")
    72  				servers = common.TestnetMapServer
    73  			} else {
    74  				pcs.Logger.Info().Msg("chain is custom public network so only custom polaris in configuration file will be used")
    75  			}
    76  
    77  			for _, addrStr := range servers {
    78  				meta, err := p2putil.FromMultiAddrString(addrStr)
    79  				if err != nil {
    80  					pcs.Logger.Info().Str("addr_str", addrStr).Msg("invalid polaris server address in base setting ")
    81  					continue
    82  				}
    83  				pcs.mapServers = append(pcs.mapServers, meta)
    84  			}
    85  		} else {
    86  			pcs.Logger.Info().Msg("chain is private so only using polaris in config file")
    87  		}
    88  		// append custom polarises set in configuration file
    89  		for _, addrStr := range cfg.NPAddPolarises {
    90  			meta, err := p2putil.FromMultiAddrString(addrStr)
    91  			if err != nil {
    92  				pcs.Logger.Info().Str("addr_str", addrStr).Msg("invalid polaris server address in config file ")
    93  				continue
    94  			}
    95  			pcs.mapServers = append(pcs.mapServers, meta)
    96  		}
    97  
    98  		if len(pcs.mapServers) == 0 {
    99  			pcs.Logger.Warn().Msg("using Polaris is enabled but no active polaris server found. node discovery by polaris will not works well")
   100  		} else {
   101  			pcs.Logger.Info().Array("polarises", p2putil.NewLogPeerMetasMarshaller(pcs.mapServers, 10)).Msg("using Polaris")
   102  		}
   103  	} else {
   104  		pcs.Logger.Info().Msg("node discovery by Polaris is disabled by configuration.")
   105  	}
   106  }
   107  
   108  func (pcs *PolarisConnectSvc) BeforeStart() {}
   109  
   110  func (pcs *PolarisConnectSvc) AfterStart() {
   111  	pcs.nt = pcs.ntc.GetNetworkTransport()
   112  	pcs.nt.AddStreamHandler(common.PolarisPingSub, pcs.onPing)
   113  
   114  }
   115  
   116  func (pcs *PolarisConnectSvc) BeforeStop() {
   117  	pcs.nt.RemoveStreamHandler(common.PolarisPingSub)
   118  }
   119  
   120  func (pcs *PolarisConnectSvc) Statistics() *map[string]interface{} {
   121  	return nil
   122  	//dummy := make(map[string]interface{})
   123  	//return &dummy
   124  }
   125  
   126  func (pcs *PolarisConnectSvc) Receive(context actor.Context) {
   127  	rawMsg := context.Message()
   128  	switch msg := rawMsg.(type) {
   129  	case *message.MapQueryMsg:
   130  		pcs.Hub().Tell(message.P2PSvc, pcs.queryPeers(msg))
   131  	default:
   132  		//		pcs.Logger.Debug().Interface("msg", msg) // TODO: temporal code for resolve compile error
   133  	}
   134  }
   135  
   136  func (pcs *PolarisConnectSvc) queryPeers(msg *message.MapQueryMsg) *message.MapQueryRsp {
   137  	succ := 0
   138  	resultPeers := make([]*types.PeerAddress, 0, msg.Count)
   139  	for _, meta := range pcs.mapServers {
   140  		addrs, err := pcs.connectAndQuery(meta, msg.BestBlock.Hash, msg.BestBlock.Header.BlockNo)
   141  		if err != nil {
   142  			if err == ErrTooLowVersion {
   143  				pcs.Logger.Error().Err(err).Str("polarisID", p2putil.ShortForm(meta.ID)).Msg("Polaris responded this aergosvr is too low, check and upgrade aergosvr")
   144  			} else {
   145  				pcs.Logger.Warn().Err(err).Str("polarisID", p2putil.ShortForm(meta.ID)).Msg("failed to get peer addresses")
   146  			}
   147  			continue
   148  		}
   149  		// FIXME delete duplicated peers
   150  		resultPeers = append(resultPeers, addrs...)
   151  		succ++
   152  	}
   153  	err := error(nil)
   154  	if succ == 0 {
   155  		err = fmt.Errorf("all servers of polaris are down")
   156  	}
   157  	pcs.Logger.Debug().Int("peer_cnt", len(resultPeers)).Msg("Got map response and send back")
   158  	resp := &message.MapQueryRsp{Peers: resultPeers, Err: err}
   159  	return resp
   160  }
   161  
   162  func (pcs *PolarisConnectSvc) connectAndQuery(mapServerMeta p2pcommon.PeerMeta, bestHash []byte, bestHeight uint64) ([]*types.PeerAddress, error) {
   163  	s, err := pcs.nt.GetOrCreateStreamWithTTL(mapServerMeta, common.PolarisConnectionTTL, common.PolarisMapSub)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	defer s.Close()
   168  
   169  	peerID := s.Conn().RemotePeer()
   170  	if peerID != mapServerMeta.ID {
   171  		return nil, fmt.Errorf("internal error peerid mismatch, exp %s, actual %s", mapServerMeta.ID.Pretty(), peerID.Pretty())
   172  	}
   173  	pcs.Logger.Debug().Str("polarisID", peerID.String()).Msg("Sending map query")
   174  
   175  	rw := v030.NewV030ReadWriter(bufio.NewReader(s), bufio.NewWriter(s), nil)
   176  
   177  	peerAddress := pcs.ntc.SelfMeta().ToPeerAddress()
   178  	chainBytes, _ := pcs.ntc.ChainID().Bytes()
   179  	peerStatus := &types.Status{Sender: &peerAddress, BestBlockHash: bestHash, BestHeight: bestHeight, ChainID: chainBytes,
   180  		Version: p2pkey.NodeVersion()}
   181  
   182  	return pcs.queryToPolaris(mapServerMeta, rw, peerStatus)
   183  }
   184  
   185  func (pcs *PolarisConnectSvc) queryToPolaris(mapServerMeta p2pcommon.PeerMeta, rw p2pcommon.MsgReadWriter, peerStatus *types.Status) ([]*types.PeerAddress, error) {
   186  	// receive input
   187  	err := pcs.sendRequest(peerStatus, mapServerMeta, pcs.exposeself, 100, rw)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	_, resp, err := pcs.readResponse(mapServerMeta, rw)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	switch resp.Status {
   196  	case types.ResultStatus_OK:
   197  		return resp.Addresses, nil
   198  	case types.ResultStatus_FAILED_PRECONDITION:
   199  		if resp.Message == common.TooOldVersionMsg {
   200  			return nil, ErrTooLowVersion
   201  		} else {
   202  			return nil, fmt.Errorf("remote error %s", resp.Status.String())
   203  		}
   204  	default:
   205  		return nil, fmt.Errorf("remote error %s", resp.Status.String())
   206  	}
   207  }
   208  
   209  func (pcs *PolarisConnectSvc) sendRequest(status *types.Status, mapServerMeta p2pcommon.PeerMeta, register bool, size int, wt p2pcommon.MsgReadWriter) error {
   210  	msgID := p2pcommon.NewMsgID()
   211  	queryReq := &types.MapQuery{Status: status, Size: int32(size), AddMe: register, Excludes: [][]byte{[]byte(mapServerMeta.ID)}}
   212  	bytes, err := p2putil.MarshalMessageBody(queryReq)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	reqMsg := common.NewPolarisMessage(msgID, common.MapQuery, bytes)
   217  
   218  	return wt.WriteMsg(reqMsg)
   219  }
   220  
   221  // tryAddPeer will do check connecting peer and add. it will return peer meta information received from
   222  // remote peer setup some
   223  func (pcs *PolarisConnectSvc) readResponse(mapServerMeta p2pcommon.PeerMeta, rd p2pcommon.MsgReadWriter) (p2pcommon.Message, *types.MapResponse, error) {
   224  	data, err := rd.ReadMsg()
   225  	if err != nil {
   226  		return nil, nil, err
   227  	}
   228  	queryResp := &types.MapResponse{}
   229  	err = p2putil.UnmarshalMessageBody(data.Payload(), queryResp)
   230  	if err != nil {
   231  		return data, nil, err
   232  	}
   233  	pcs.Logger.Debug().Str(p2putil.LogPeerID, mapServerMeta.ID.String()).Int("peer_cnt", len(queryResp.Addresses)).Msg("Received map query response")
   234  
   235  	return data, queryResp, nil
   236  }
   237  
   238  func (pcs *PolarisConnectSvc) onPing(s network.Stream) {
   239  	peerID := s.Conn().RemotePeer()
   240  	pcs.Logger.Debug().Str("polarisID", peerID.String()).Msg("Received ping from polaris (maybe)")
   241  
   242  	rw := v030.NewV030ReadWriter(bufio.NewReader(s), bufio.NewWriter(s), nil)
   243  	defer s.Close()
   244  
   245  	req, err := rw.ReadMsg()
   246  	if err != nil {
   247  		return
   248  	}
   249  	pingReq := &types.Ping{}
   250  	err = p2putil.UnmarshalMessageBody(req.Payload(), pingReq)
   251  	if err != nil {
   252  		return
   253  	}
   254  	// TODO: check if sender is known polaris or peer and it not, ban or write to blacklist .
   255  	pingResp := &types.Ping{}
   256  	bytes, err := p2putil.MarshalMessageBody(pingResp)
   257  	if err != nil {
   258  		return
   259  	}
   260  	msgID := p2pcommon.NewMsgID()
   261  	respMsg := common.NewPolarisRespMessage(msgID, req.ID(), p2pcommon.PingResponse, bytes)
   262  	err = rw.WriteMsg(respMsg)
   263  	if err != nil {
   264  		return
   265  	}
   266  }