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  }