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 }