以太坊细节[1]:消息机制
2021-05-29

前言

研究公链中msg互相触发关系,学习一下以太坊中消息广播的逻辑顺序。
带宽要求和消息转发之间有没有相对关系。

消息监听机制 handleMsg: server,client,ProtocolManager
消息广播机制 broadcast: transaction,block,blockHash

寻找转发点

找msg的监听循环,代码中类似handleMsg之类的函数

ProtocolManager.handeMsg 中的转发逻辑

在ProtocolManager.handleMsg中把所有消息的触发关系梳理了一遍。
内容如下

举例 GetBlockHeadersMsg

在共识过程中,如果节点收到了区块头的消息。
会触发消息监听中的GetBlockHeadersMsg事件。
代码如下
handler.go line386

case msg.Code == GetBlockHeadersMsg:
        ...
        if err := msg.Decode(&query); err != nil {
			return errResp(ErrDecode, "%v: %v", msg, err)
		}
        ...
return p.SendBlockHeaders(headers)

其中peer.SendBlockHeaders函数的定义如下

func (p *peer) SendBlockHeaders(headers []*types.Header) error {
	return p2p.Send(p.rw, BlockHeadersMsg, headers)
}

emmmm,就是简单地把区块头广播出去。

在return之前还有一个返回点

if err := msg.Decode(&query); err != nil {
			return errResp(ErrDecode, "%v: %v", msg, err)
		}

可见节点收到的区块头会被广播出去的条件是msg要能够正常的Decode.

其他msg分析过程同上。

serverHandler.handleMsg

serverHandler的监听中,所有消息触发情况如下图。

哭了,没有消息闭环。

clientHandler.handleMsg

clientHandler的监听,作用是对Server返回的结果进行对应的处理
收到的消息及其对应关系如下。

寻找广播点

广播的底层在于peer类的boardcast函数

func (p *peer) broadcast() {
	for {
		select {
		case txs := <-p.queuedTxs:
			if err := p.SendTransactions(txs); err != nil {
				return
			}
			p.Log().Trace("Broadcast transactions", "count", len(txs))

		case prop := <-p.queuedProps:
			if err := p.SendNewBlock(prop.block, prop.td); err != nil {
				return
			}
			p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td)

		case block := <-p.queuedAnns:
			if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil {
				return
			}
			p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash())

		case <-p.term:
			return
		}
	}
}

emmmmmmm 为啥刚才没搜到

三个channel用于广播

p.queuedProps 广播块
p.queuedAnns 广播哈希
p.queuedTxs 广播交易

广播交易 广播哈希

当节点自己挖到block时
w.mux.Post(core.NewMinedBlockEvent{Block: block})
其中会在本地生成NewMinedBlockEvent事件
该事件触发minedBroadcastLoop循环

		if ev, ok := obj.Data.(core.NewMinedBlockEvent); ok {
			pm.BroadcastBlock(ev.Block, true)  // First propagate block to peers
			pm.BroadcastBlock(ev.Block, false) // Only then announce to the rest
		}

其中BroadcastBlock函数根据第二个参数,选择广播整个区块还是只广播区块的hash

交易广播

广播交易监听txBroadcastLoop循环

select {
		case event := <-pm.txsCh:
			pm.BroadcastTxs(event.Txs)

触发广播需要pm.txsCh
然而没找到这个信道的赋值在哪

带宽消耗

文献描述

试图从以太坊的白皮书中找到对带宽的描述
https://github.com/ethereum/wiki/wiki/[中文]-以太坊白皮书
但是并没有相关内容
更多是在描述原理

查到了bitmex网,这里有一些对节点性能的测量标准。
BitMEX 研究推出以太坊节点指标网站——Nodestats.org

https://blog.bitmex.com/zh_cn-bitmex-research-launches-ethereum-node-monitoring-website-nodestats-org/

从中我们可以找到一些geth的节点带宽要求

Geth的上行带宽 要求 4kb/s
Geth的下行带宽 要求 60kb/s

代码中的限制

rlpx.go line603

if fsize > maxUint24 {

每个msg最大256byte。