github.com/turingchain2020/turingchain@v1.1.21/system/p2p/dht/protocol/broadcast/pubsub.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 6 7 import ( 8 "encoding/hex" 9 "runtime" 10 "time" 11 12 "github.com/turingchain2020/turingchain/p2p/utils" 13 net "github.com/turingchain2020/turingchain/system/p2p/dht/extension" 14 "github.com/turingchain2020/turingchain/types" 15 "github.com/golang/snappy" 16 ) 17 18 const ( 19 psTxTopic = "tx/v1.0.0" 20 psBlockTopic = "block/v1.0.0" 21 ) 22 23 // 基于libp2p pubsub插件广播 24 type pubSub struct { 25 *broadcastProtocol 26 } 27 28 // new pub sub 29 func newPubSub(b *broadcastProtocol) *pubSub { 30 return &pubSub{b} 31 } 32 33 // 广播入口函数,处理相关初始化 34 func (p *pubSub) broadcast() { 35 36 //TODO check net is sync 37 38 txIncoming := make(chan net.SubMsg, 1024) //交易接收通道, 订阅外部广播消息 39 txOutgoing := p.ps.Sub(psTxTopic) //交易发送通道, 订阅内部广播消息 40 //区块 41 blockIncoming := make(chan net.SubMsg, 128) 42 blockOutgoing := p.ps.Sub(psBlockTopic) 43 44 // pub sub topic注册 45 err := p.Pubsub.JoinAndSubTopic(psTxTopic, p.callback(txIncoming)) 46 if err != nil { 47 log.Error("pubsub broadcast", "join tx topic err", err) 48 return 49 } 50 err = p.Pubsub.JoinAndSubTopic(psBlockTopic, p.callback(blockIncoming)) 51 if err != nil { 52 log.Error("pubsub broadcast", "join block topic err", err) 53 return 54 } 55 56 // 不存在订阅topic的节点时,不开启广播,目前只在初始化时做判定 57 for len(p.Pubsub.FetchTopicPeers(psTxTopic)) == 0 { 58 time.Sleep(time.Second * 2) 59 log.Warn("pub sub broadcast", "info", "no peers available") 60 } 61 62 //发送和接收用多个函数并发处理,提高效率 63 //交易广播, 使用多个协程并发处理,提高效率 64 cpu := runtime.NumCPU() 65 for i := 0; i < cpu; i++ { 66 go p.handlePubMsg(psTxTopic, txOutgoing) 67 go p.handleSubMsg(psTxTopic, txIncoming, p.txFilter) 68 } 69 70 //区块广播 71 go p.handlePubMsg(psBlockTopic, blockOutgoing) 72 go p.handleSubMsg(psBlockTopic, blockIncoming, p.blockFilter) 73 } 74 75 // 处理广播消息发布 76 func (p *pubSub) handlePubMsg(topic string, out chan interface{}) { 77 78 defer p.ps.Unsub(out) 79 buf := make([]byte, 0) 80 var err error 81 for { 82 select { 83 case data, ok := <-out: //发送广播交易 84 if !ok { 85 return 86 } 87 msg := data.(types.Message) 88 raw := p.encodeMsg(msg, &buf) 89 if err != nil { 90 log.Error("handlePubMsg", "topic", topic, "hash", p.getMsgHash(topic, msg), "err", err) 91 break 92 } 93 94 err = p.Pubsub.Publish(topic, raw) 95 if err != nil { 96 log.Error("handlePubMsg", "topic", topic, "publish err", err) 97 } 98 99 case <-p.Ctx.Done(): 100 return 101 } 102 } 103 } 104 105 // 处理广播消息订阅 106 func (p *pubSub) handleSubMsg(topic string, in chan net.SubMsg, filter *utils.Filterdata) { 107 108 buf := make([]byte, 0) 109 var err error 110 var msg types.Message 111 for { 112 select { 113 case data, ok := <-in: //接收广播交易 114 if !ok { 115 return 116 } 117 msg = p.newMsg(topic) 118 err = p.decodeMsg(data.Data, &buf, msg) 119 if err != nil { 120 log.Error("handleSubMsg", "topic", topic, "decodeMsg err", err) 121 break 122 } 123 hash := p.getMsgHash(topic, msg) 124 // 接收重复检测, 125 if filter.Contains(hash) { 126 break 127 } 128 129 //TODO 保存消息源节点,对错误消息需要拉黑处理 130 filter.Add(hash, struct{}{}) 131 132 // 将接收的交易或区块 转发到内部对应模块 133 if topic == psTxTopic { 134 err = p.postMempool(hash, msg.(*types.Transaction)) 135 } else { 136 err = p.postBlockChain(hash, data.ReceivedFrom.String(), msg.(*types.Block)) 137 } 138 139 if err != nil { 140 log.Error("handleSubMsg", "topic", topic, "hash", hash, "post msg err", err) 141 } 142 143 case <-p.Ctx.Done(): 144 return 145 } 146 } 147 148 } 149 150 // 统一处理哈希计算 151 func (p *pubSub) getMsgHash(topic string, msg types.Message) string { 152 if topic == psTxTopic { 153 return hex.EncodeToString(msg.(*types.Transaction).Hash()) 154 } 155 return hex.EncodeToString(msg.(*types.Block).Hash(p.ChainCfg)) 156 } 157 158 // 构造接收消息对象s 159 func (p *pubSub) newMsg(topic string) types.Message { 160 if topic == psTxTopic { 161 return &types.Transaction{} 162 } 163 return &types.Block{} 164 } 165 166 // 生成订阅消息回调 167 func (p *pubSub) callback(out chan<- net.SubMsg) net.SubCallBack { 168 return func(topic string, msg net.SubMsg) { 169 out <- msg 170 } 171 } 172 173 // 数据压缩后发送, 内部对相关数组进行重复利用 174 func (p *pubSub) encodeMsg(msg types.Message, pbuf *[]byte) []byte { 175 buf := *pbuf 176 buf = buf[:cap(buf)] 177 raw := types.Encode(msg) 178 buf = snappy.Encode(buf, raw) 179 *pbuf = buf 180 // 复用raw数组作为压缩数据返回, 需要比较容量是否够大 181 if cap(raw) >= len(buf) { 182 raw = raw[:len(buf)] 183 } else { 184 raw = make([]byte, len(buf)) 185 } 186 copy(raw, buf) 187 return raw 188 } 189 190 // 接收数据并解压缩 191 func (p *pubSub) decodeMsg(raw []byte, pbuf *[]byte, msg types.Message) error { 192 193 var err error 194 buf := *pbuf 195 buf = buf[:cap(buf)] 196 buf, err = snappy.Decode(buf, raw) 197 if err != nil { 198 log.Error("pubSub decodeMsg", "snappy decode err", err) 199 return errSnappyDecode 200 } 201 //重复利用解码buf 202 *pbuf = buf 203 err = types.Decode(buf, msg) 204 if err != nil { 205 log.Error("pubSub decodeMsg", "pb decode err", err) 206 return types.ErrDecode 207 } 208 209 return nil 210 }