github.com/turingchain2020/turingchain@v1.1.21/system/p2p/dht/protocol/broadcast/broadcast.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package broadcast broadcast protocol 6 package broadcast 7 8 import ( 9 "context" 10 "encoding/hex" 11 "sync/atomic" 12 13 "github.com/turingchain2020/turingchain/common/pubsub" 14 15 "github.com/turingchain2020/turingchain/p2p/utils" 16 17 "github.com/libp2p/go-libp2p-core/peer" 18 19 "github.com/turingchain2020/turingchain/common/log/log15" 20 "github.com/turingchain2020/turingchain/queue" 21 "github.com/turingchain2020/turingchain/system/p2p/dht/protocol" 22 p2pty "github.com/turingchain2020/turingchain/system/p2p/dht/types" 23 "github.com/turingchain2020/turingchain/types" 24 ) 25 26 var log = log15.New("module", "p2p.broadcast") 27 28 const ( 29 broadcastV1 = "/turingchain/p2p/broadcast/1.0.0" 30 ) 31 32 func init() { 33 protocol.RegisterProtocolInitializer(InitProtocol) 34 } 35 36 // 37 type broadcastProtocol struct { 38 *protocol.P2PEnv 39 40 txFilter *utils.Filterdata 41 blockFilter *utils.Filterdata 42 txSendFilter *utils.Filterdata 43 blockSendFilter *utils.Filterdata 44 ltBlockCache *utils.SpaceLimitCache 45 p2pCfg *p2pty.P2PSubConfig 46 broadcastPeers map[peer.ID]context.CancelFunc 47 ps *pubsub.PubSub 48 exitPeer chan peer.ID 49 errPeer chan peer.ID 50 // 接收V1版本节点 51 peerV1 chan peer.ID 52 peerV1Num int32 53 } 54 55 // InitProtocol init protocol 56 func InitProtocol(env *protocol.P2PEnv) { 57 new(broadcastProtocol).init(env) 58 } 59 60 func (p *broadcastProtocol) init(env *protocol.P2PEnv) { 61 p.P2PEnv = env 62 //接收交易和区块过滤缓存, 避免重复提交到mempool或blockchain 63 p.txFilter = utils.NewFilter(txRecvFilterCacheNum) 64 p.blockFilter = utils.NewFilter(blockRecvFilterCacheNum) 65 66 //发送交易和区块时过滤缓存, 解决冗余广播发送 67 p.txSendFilter = utils.NewFilter(txSendFilterCacheNum) 68 p.blockSendFilter = utils.NewFilter(blockSendFilterCacheNum) 69 p.ps = pubsub.NewPubSub(10000) 70 p.exitPeer = make(chan peer.ID) 71 p.errPeer = make(chan peer.ID) 72 p.peerV1 = make(chan peer.ID, 5) 73 p.broadcastPeers = make(map[peer.ID]context.CancelFunc) 74 // 单独复制一份, 避免data race 75 subCfg := *(env.SubConfig) 76 77 //ttl至少设为2 78 if subCfg.LightTxTTL <= 1 { 79 subCfg.LightTxTTL = defaultLtTxBroadCastTTL 80 } 81 if subCfg.MaxTTL <= 0 { 82 subCfg.MaxTTL = defaultMaxTxBroadCastTTL 83 } 84 if subCfg.MinLtBlockSize <= 0 { 85 subCfg.MinLtBlockSize = defaultMinLtBlockSize 86 } 87 if subCfg.LtBlockCacheSize <= 0 { 88 subCfg.LtBlockCacheSize = defaultLtBlockCacheSize 89 } 90 91 // 老版本保持兼容性, 默认最多选择5个节点广播 92 if subCfg.MaxBroadcastPeers <= 0 { 93 subCfg.MaxBroadcastPeers = 5 94 } 95 96 //接收到短哈希区块数据,只构建出区块部分交易,需要缓存, 并继续向对端节点请求剩余数据 97 //内部组装成功失败或成功都会进行清理,实际运行并不会长期占用内存,只要限制极端情况最大值 98 p.ltBlockCache = utils.NewSpaceLimitCache(ltBlockCacheNum, int(subCfg.LtBlockCacheSize*1024*1024)) 99 p.p2pCfg = &subCfg 100 101 protocol.RegisterStreamHandler(p.Host, broadcastV1, protocol.HandlerWithClose(p.handleStreamBroadcastV1)) 102 //注册事件处理函数 103 protocol.RegisterEventHandler(types.EventTxBroadcast, p.handleBroadCastEvent) 104 protocol.RegisterEventHandler(types.EventBlockBroadcast, p.handleBroadCastEvent) 105 106 // pub sub broadcast 107 go newPubSub(p).broadcast() 108 go p.manageBroadcastV1Peer() 109 } 110 111 // 处理系统广播发送事件,交易及区块 112 func (p *broadcastProtocol) handleBroadCastEvent(msg *queue.Message) { 113 114 var sendData interface{} 115 var topic, hash string 116 var filter *utils.Filterdata 117 if tx, ok := msg.GetData().(*types.Transaction); ok { 118 hash = hex.EncodeToString(tx.Hash()) 119 filter = p.txFilter 120 topic = psTxTopic 121 //兼容老版本,总是转发全交易 122 route := &types.P2PRoute{TTL: 1} 123 sendData = &types.P2PTx{Tx: tx, Route: route} 124 } else if block, ok := msg.GetData().(*types.Block); ok { 125 hash = hex.EncodeToString(block.Hash(p.ChainCfg)) 126 filter = p.blockFilter 127 topic = psBlockTopic 128 sendData = &types.P2PBlock{Block: block} 129 } else { 130 log.Error("handleBroadCastEvent", "receive unexpect msg", msg) 131 return 132 } 133 //目前p2p可能存在多个插件并存,dht和gossip,消息回收容易混乱,需要进一步梳理 TODO:p2p模块热点区域消息回收 134 //p.QueueClient.FreeMessage(msg) 135 136 // pub sub只需要转发本节点产生的交易或区块 137 if !filter.Contains(hash) { 138 filter.Add(hash, struct{}{}) 139 p.ps.FIFOPub(msg.GetData(), topic) 140 } 141 142 //发布到老版本接收通道 143 if atomic.LoadInt32(&p.peerV1Num) > 0 { 144 p.ps.FIFOPub(sendData, bcTopic) 145 } 146 } 147 148 // 发送广播数据到节点, 支持延迟关闭内部stream,主要考虑多个节点并行发送情况,不需要等待关闭 149 func (p *broadcastProtocol) sendPeer(data interface{}, pid, version string) error { 150 151 //if version == broadcastV2 { 152 // p.ps.Pub(data, pid) 153 // return nil 154 //} 155 // broadcast v1 TODO 版本升级后移除代码 156 sendData, doSend := p.handleSend(data, pid) 157 if !doSend { 158 return nil 159 } 160 //包装一层MessageBroadCast 161 broadData := &types.MessageBroadCast{ 162 Message: sendData} 163 164 rawPid, err := peer.Decode(pid) 165 if err != nil { 166 log.Error("sendPeer", "id", pid, "decode pid err", err) 167 return err 168 } 169 stream, err := p.Host.NewStream(p.Ctx, rawPid, broadcastV1) 170 if err != nil { 171 log.Error("sendPeer", "id", pid, "NewStreamErr", err) 172 return err 173 } 174 175 err = protocol.WriteStream(broadData, stream) 176 if err != nil { 177 log.Error("sendPeer", "pid", pid, "WriteStream err", err) 178 return err 179 } 180 protocol.CloseStream(stream) 181 return nil 182 } 183 184 // handleSend 对数据进行处理,包装成BroadCast结构 185 func (p *broadcastProtocol) handleSend(rawData interface{}, pid string) (sendData *types.BroadCastData, doSend bool) { 186 //出错处理 187 defer func() { 188 if r := recover(); r != nil { 189 log.Error("handleSend_Panic", "sendData", rawData, "pid", pid, "recoverErr", r) 190 doSend = false 191 } 192 }() 193 sendData = &types.BroadCastData{} 194 195 doSend = false 196 if tx, ok := rawData.(*types.P2PTx); ok { 197 doSend = p.sendTx(tx, sendData, pid) 198 } else if blc, ok := rawData.(*types.P2PBlock); ok { 199 doSend = p.sendBlock(blc, sendData, pid) 200 } else if query, ok := rawData.(*types.P2PQueryData); ok { 201 doSend = p.sendQueryData(query, sendData, pid) 202 } else if rep, ok := rawData.(*types.P2PBlockTxReply); ok { 203 doSend = p.sendQueryReply(rep, sendData, pid) 204 } 205 return 206 } 207 208 func (p *broadcastProtocol) handleReceive(data *types.BroadCastData, pid, peerAddr, version string) (err error) { 209 210 //接收网络数据不可靠 211 defer func() { 212 if r := recover(); r != nil { 213 log.Error("handleReceive_Panic", "recvData", data, "pid", pid, "addr", peerAddr, "recoverErr", r) 214 } 215 }() 216 if tx := data.GetTx(); tx != nil { 217 err = p.recvTx(tx, pid) 218 } else if ltTx := data.GetLtTx(); ltTx != nil { 219 err = p.recvLtTx(ltTx, pid, peerAddr, version) 220 } else if ltBlc := data.GetLtBlock(); ltBlc != nil { 221 err = p.recvLtBlock(ltBlc, pid, peerAddr, version) 222 } else if blc := data.GetBlock(); blc != nil { 223 err = p.recvBlock(blc, pid, peerAddr) 224 } else if query := data.GetQuery(); query != nil { 225 err = p.recvQueryData(query, pid, peerAddr, version) 226 } else if rep := data.GetBlockRep(); rep != nil { 227 err = p.recvQueryReply(rep, pid, peerAddr, version) 228 } 229 if err != nil { 230 log.Error("handleReceive", "pid", pid, "addr", peerAddr, "recvData", data.Value, "err", err) 231 } 232 return 233 } 234 235 func (p *broadcastProtocol) postBlockChain(blockHash, pid string, block *types.Block) error { 236 return p.P2PManager.PubBroadCast(blockHash, &types.BlockPid{Pid: pid, Block: block}, types.EventBroadcastAddBlock) 237 } 238 239 func (p *broadcastProtocol) postMempool(txHash string, tx *types.Transaction) error { 240 return p.P2PManager.PubBroadCast(txHash, tx, types.EventTx) 241 } 242 243 type sendFilterInfo struct { 244 //记录广播交易或区块时需要忽略的节点, 这些节点可能是交易的来源节点,也可能节点间维护了多条连接, 冗余发送 245 ignoreSendPeers map[string]bool 246 } 247 248 //检测是否冗余发送, 或者添加到发送过滤(内部存在直接修改读写保护的数据, 对filter lru的读写需要外层锁保护) 249 func addIgnoreSendPeerAtomic(filter *utils.Filterdata, key string, pid string) (exist bool) { 250 251 filter.GetAtomicLock() 252 defer filter.ReleaseAtomicLock() 253 var info *sendFilterInfo 254 if !filter.Contains(key) { //之前没有收到过这个key 255 info = &sendFilterInfo{ignoreSendPeers: make(map[string]bool)} 256 filter.Add(key, info) 257 } else { 258 data, _ := filter.Get(key) 259 info = data.(*sendFilterInfo) 260 } 261 _, exist = info.ignoreSendPeers[pid] 262 info.ignoreSendPeers[pid] = true 263 return exist 264 } 265 266 // 删除发送过滤器记录 267 func removeIgnoreSendPeerAtomic(filter *utils.Filterdata, key, pid string) { 268 269 filter.GetAtomicLock() 270 defer filter.ReleaseAtomicLock() 271 if filter.Contains(key) { 272 data, _ := filter.Get(key) 273 info := data.(*sendFilterInfo) 274 delete(info.ignoreSendPeers, pid) 275 } 276 }