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  }