github.com/turingchain2020/turingchain@v1.1.21/system/p2p/dht/protocol/p2pstore/p2pstore.go (about)

     1  package p2pstore
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/turingchain2020/turingchain/common/log/log15"
    10  	"github.com/turingchain2020/turingchain/system/p2p/dht/protocol"
    11  	types2 "github.com/turingchain2020/turingchain/system/p2p/dht/types"
    12  	"github.com/turingchain2020/turingchain/types"
    13  	"github.com/libp2p/go-libp2p-core/discovery"
    14  	"github.com/libp2p/go-libp2p-core/peer"
    15  	dht "github.com/libp2p/go-libp2p-kad-dht"
    16  	kb "github.com/libp2p/go-libp2p-kbucket"
    17  )
    18  
    19  const (
    20  	fetchShardPeer = "/turingchain/fetch-shard-peer/1.0.0"
    21  	fetchChunk     = "/turingchain/fetch-chunk/1.0.0"
    22  	storeChunk     = "/turingchain/store-chunk/1.0.0"
    23  	getHeader      = "/turingchain/headers/1.0.0"
    24  	getChunkRecord = "/turingchain/chunk-record/1.0.0"
    25  	fullNode       = "/turingchain/full-node/1.0.0"
    26  	// Deprecated: old version, use getHeader instead
    27  	getHeaderOld = "/turingchain/headerinfoReq/1.0.0"
    28  )
    29  
    30  const maxConcurrency = 10
    31  
    32  var log = log15.New("module", "protocol.p2pstore")
    33  var backup = 20
    34  
    35  //Protocol ...
    36  type Protocol struct {
    37  	*protocol.P2PEnv //协议共享接口变量
    38  
    39  	//cache the notify message when other peers notify this node to store chunk.
    40  	notifying sync.Map
    41  	//send the message in <notifying> to this queue if chunk does not exist.
    42  	notifyingQueue chan *types.ChunkInfoMsg
    43  	//chunks that full node can provide without checking.
    44  	chunkWhiteList sync.Map
    45  
    46  	//a child table of healthy routing table without full nodes
    47  	ShardHealthyRoutingTable *kb.RoutingTable
    48  
    49  	//本节点保存的chunk的索引表,会随着网络拓扑结构的变化而变化
    50  	localChunkInfo      map[string]LocalChunkInfo
    51  	localChunkInfoMutex sync.RWMutex
    52  
    53  	concurrency int64
    54  }
    55  
    56  func init() {
    57  	protocol.RegisterProtocolInitializer(InitProtocol)
    58  }
    59  
    60  //InitProtocol initials the protocol.
    61  func InitProtocol(env *protocol.P2PEnv) {
    62  	p := &Protocol{
    63  		P2PEnv:                   env,
    64  		ShardHealthyRoutingTable: kb.NewRoutingTable(dht.KValue*2, kb.ConvertPeerID(env.Host.ID()), time.Minute, env.Host.Peerstore()),
    65  		notifyingQueue:           make(chan *types.ChunkInfoMsg, 1024),
    66  	}
    67  	//
    68  	if env.SubConfig.Backup > 1 {
    69  		backup = env.SubConfig.Backup
    70  	}
    71  	// RoutingTable更新时同时更新ShardHealthyRoutingTable
    72  	p.RoutingTable.PeerRemoved = func(id peer.ID) {
    73  		p.ShardHealthyRoutingTable.Remove(id)
    74  	}
    75  	go p.updateShardHealthyRoutingTableRoutine()
    76  	p.initLocalChunkInfoMap()
    77  
    78  	//注册p2p通信协议,用于处理节点之间请求
    79  	protocol.RegisterStreamHandler(p.Host, fetchShardPeer, protocol.HandlerWithRW(p.handleStreamFetchShardPeers))
    80  	protocol.RegisterStreamHandler(p.Host, getHeaderOld, p.handleStreamGetHeaderOld)
    81  	protocol.RegisterStreamHandler(p.Host, getHeader, protocol.HandlerWithAuthAndSign(p.handleStreamGetHeader))
    82  	if !p.SubConfig.DisableShard {
    83  		protocol.RegisterStreamHandler(p.Host, fullNode, protocol.HandlerWithWrite(p.handleStreamIsFullNode))
    84  		protocol.RegisterStreamHandler(p.Host, fetchChunk, p.handleStreamFetchChunk) //数据较大,采用特殊写入方式
    85  		protocol.RegisterStreamHandler(p.Host, storeChunk, protocol.HandlerWithAuth(p.handleStreamStoreChunks))
    86  		protocol.RegisterStreamHandler(p.Host, getChunkRecord, protocol.HandlerWithAuthAndSign(p.handleStreamGetChunkRecord))
    87  	}
    88  	//同时注册eventHandler,用于处理blockchain模块发来的请求
    89  	protocol.RegisterEventHandler(types.EventNotifyStoreChunk, p.handleEventNotifyStoreChunk)
    90  	protocol.RegisterEventHandler(types.EventGetChunkBlock, p.handleEventGetChunkBlock)
    91  	protocol.RegisterEventHandler(types.EventGetChunkBlockBody, p.handleEventGetChunkBlockBody)
    92  	protocol.RegisterEventHandler(types.EventGetChunkRecord, p.handleEventGetChunkRecord)
    93  	protocol.RegisterEventHandler(types.EventFetchBlockHeaders, p.handleEventGetHeaders)
    94  
    95  	go p.syncRoutine()
    96  	go func() {
    97  		ticker1 := time.NewTicker(time.Minute)
    98  		ticker2 := time.NewTicker(types2.RefreshInterval)
    99  		ticker4 := time.NewTicker(time.Hour)
   100  
   101  		for {
   102  			select {
   103  			case <-p.Ctx.Done():
   104  				return
   105  			case <-ticker1.C:
   106  				p.updateChunkWhiteList()
   107  				p.advertiseFullNode()
   108  			case <-ticker2.C:
   109  				p.republish()
   110  			case <-ticker4.C:
   111  				//debug info
   112  				p.localChunkInfoMutex.Lock()
   113  				log.Info("debugLocalChunk", "local chunk hash len", len(p.localChunkInfo))
   114  				p.localChunkInfoMutex.Unlock()
   115  				p.debugFullNode()
   116  				log.Info("debug rt peers", "======== amount", p.RoutingTable.Size())
   117  				log.Info("debug shard healthy peers", "======== amount", p.ShardHealthyRoutingTable.Size())
   118  				log.Info("debug length", "notifying msg len", len(p.notifyingQueue))
   119  				log.Info("debug peers and conns", "peers len", len(p.Host.Network().Peers()), "conns len", len(p.Host.Network().Conns()))
   120  				//for _, conn := range p.Host.Network().Conns() {
   121  				//	streams := conn.GetStreams()
   122  				//	log.Info("debug new conn", "remote peer", conn.RemotePeer(), "len streams", len(streams))
   123  				//	for _, s := range streams {
   124  				//		log.Info("debug new stream", "protocol id", s.Protocol())
   125  				//	}
   126  				//}
   127  				p.localChunkInfoMutex.RLock()
   128  				for hash, info := range p.localChunkInfo {
   129  					log.Info("localChunkInfo", "hash", hash, "start", info.Start, "end", info.End)
   130  				}
   131  				p.localChunkInfoMutex.RUnlock()
   132  			}
   133  		}
   134  	}()
   135  }
   136  
   137  func (p *Protocol) syncRoutine() {
   138  	for {
   139  		select {
   140  		case <-p.Ctx.Done():
   141  			return
   142  		case info := <-p.notifyingQueue:
   143  			p.syncChunk(info)
   144  			p.notifying.Delete(hex.EncodeToString(info.ChunkHash))
   145  		}
   146  	}
   147  }
   148  
   149  func (p *Protocol) syncChunk(info *types.ChunkInfoMsg) {
   150  	//检查本地 p2pStore,如果已存在数据则直接更新
   151  	if err := p.updateChunk(info); err == nil {
   152  		return
   153  	}
   154  
   155  	var bodys *types.BlockBodys
   156  	bodys, _ = p.getChunkFromBlockchain(info)
   157  	if bodys == nil {
   158  		//blockchain模块没有数据,从网络中搜索数据
   159  		bodys, _, _ = p.mustFetchChunk(p.Ctx, info, true)
   160  	}
   161  	if bodys == nil {
   162  		log.Error("syncChunk error", "chunkhash", hex.EncodeToString(info.ChunkHash), "start", info.Start)
   163  		return
   164  	}
   165  	if err := p.addChunkBlock(info, bodys); err != nil {
   166  		log.Error("syncChunk", "addChunkBlock error", err)
   167  		return
   168  	}
   169  	log.Info("syncChunk", "chunkhash", hex.EncodeToString(info.ChunkHash), "start", info.Start)
   170  }
   171  
   172  func (p *Protocol) updateChunkWhiteList() {
   173  	p.chunkWhiteList.Range(func(k, v interface{}) bool {
   174  		t := v.(time.Time)
   175  		if time.Since(t) > time.Minute*10 {
   176  			p.chunkWhiteList.Delete(k)
   177  		}
   178  		return true
   179  	})
   180  }
   181  
   182  func (p *Protocol) advertiseFullNode(opts ...discovery.Option) {
   183  	if !p.SubConfig.IsFullNode {
   184  		return
   185  	}
   186  	reply, err := p.API.IsSync()
   187  	if err != nil || !reply.IsOk {
   188  		// 没有同步完,不进行Advertise操作
   189  		return
   190  	}
   191  	_, err = p.Advertise(p.Ctx, fullNode, opts...)
   192  	if err != nil {
   193  		log.Error("advertiseFullNode", "error", err)
   194  	}
   195  }
   196  
   197  //TODO
   198  //debug info, to delete soon
   199  func (p *Protocol) debugFullNode() {
   200  	ctx, cancel := context.WithTimeout(p.Ctx, time.Second*3)
   201  	defer cancel()
   202  	peerInfos, err := p.FindPeers(ctx, fullNode)
   203  	if err != nil {
   204  		log.Error("debugFullNode", "FindPeers error", err)
   205  		return
   206  	}
   207  	var count int
   208  	for peerInfo := range peerInfos {
   209  		count++
   210  		log.Info("debugFullNode", "ID", peerInfo.ID, "maddrs", peerInfo.Addrs)
   211  	}
   212  	log.Info("debugFullNode", "total count", count)
   213  }
   214  
   215  func (p *Protocol) updateShardHealthyRoutingTableRoutine() {
   216  	for p.RoutingTable.Size() == 0 {
   217  		time.Sleep(time.Second)
   218  	}
   219  	updateFunc := func() {
   220  		for _, pid := range p.RoutingTable.ListPeers() {
   221  			ok, height, err := p.queryFull(pid)
   222  			if err != nil || ok {
   223  				continue
   224  			}
   225  			if height > p.PeerInfoManager.PeerMaxHeight()-512 || height > p.PeerInfoManager.PeerHeight(p.Host.ID())+1024 {
   226  				_, _ = p.ShardHealthyRoutingTable.Update(pid)
   227  			}
   228  		}
   229  	}
   230  	for i := 0; i < 3; i++ {
   231  		updateFunc()
   232  		time.Sleep(time.Second)
   233  	}
   234  	ticker := time.NewTicker(time.Minute * 5)
   235  	for {
   236  		select {
   237  		case <-p.Ctx.Done():
   238  			return
   239  		case <-ticker.C:
   240  			updateFunc()
   241  		}
   242  	}
   243  }
   244  
   245  func (p *Protocol) queryFull(pid peer.ID) (bool, int64, error) {
   246  	stream, err := p.Host.NewStream(p.Ctx, pid, fullNode)
   247  	if err != nil {
   248  		return false, -1, err
   249  	}
   250  	defer protocol.CloseStream(stream)
   251  	var resp types.P2PResponse
   252  	err = protocol.ReadStream(&resp, stream)
   253  	if err != nil {
   254  		return false, -1, err
   255  	}
   256  	if reply, ok := resp.Response.(*types.P2PResponse_NodeInfo); ok {
   257  		return reply.NodeInfo.Answer, reply.NodeInfo.Height, nil
   258  	}
   259  
   260  	return false, -1, types2.ErrInvalidResponse
   261  }