filecoin技术架构分析之六:filecoin源码分析之协议层hello握手协议

本文作者:杨尉;原创作品,转载请注明出处

[上一篇链接] filecoin技术架构分析之五:5 filecoin源码协议层分析之心跳协议
[下一篇链接] filecoin技术架构分析之七:7 filecoin源码分析之协议层存储协议

目录

  • 6 filecoin源码协议层分析之hello握手协议
    • 6.1 目的
    • 6.2 源码信息
    • 6.3 源码分析
      • 6.3.1 数据结构
      • 6.3.2 方法
      • 6.3.3 函数
      • 6.3.4 实例化及业务逻辑

6.1 目的

  • 处理节点上线后的区块同步握手。

6.2 源码信息

  • version
    • master分支 619b0eb1(2019年3月2日)
  • package
    • hello
  • location
    • protocol/hello
    • node/node.go

6.3 源码分析

6.3.1 数据结构

  • 定义协议名称
// Protocol is the libp2p protocol identifier for the hello protocol.
const protocol = "/fil/hello/1.0.0"
  • 定义hello协议消息体结构
    • TipSet切片
    • TipSet高度
    • 创世区块cid
// Message is the data structure of a single message in the hello protocol.
type Message struct {
    HeaviestTipSetCids   []cid.Cid
    HeaviestTipSetHeight uint64
    GenesisHash          cid.Cid
}
  • 同步回调函数类型定义
type syncCallback func(from peer.ID, cids []cid.Cid, height uint64)
  • 获取Tipset函数类型定义
type getTipSetFunc func() types.TipSet
  • Handler结构体,当连接到其他节点的时候,其一,会发送包含本节点信息的hello 消息给对端节点; 其二, 对端也会回复一个包含对端节点信息的消息体过来。
    • host 对应libp2p上的主机
    • 创世区块cid
    • 区块同步回调函数
    • 获取TipSet的函数
// Handler implements the 'Hello' protocol handler. Upon connecting to a new
// node, we send them a message containing some information about the state of
// our chain, and receive the same information from them. This is used to
// initiate a chainsync and detect connections to forks.
type Handler struct {
    host host.Host

    genesis cid.Cid

    // chainSyncCB is called when new peers tell us about their chain
    chainSyncCB syncCallback

    // getHeaviestTipSet is used to retrieve the current heaviest tipset
    // for filling out our hello messages.
    getHeaviestTipSet getTipSetFunc
}
  • 错误的创世区块
// ErrBadGenesis is the error returned when a missmatch in genesis blocks happens.
var ErrBadGenesis = fmt.Errorf("bad genesis block")
  • 以上基本是作为hello客户端的一些定义,以下作为hello服务端的一些定义
// New peer connection notifications
type helloNotify Handler

// 连接超时时间
const helloTimeout = time.Second * 10

6.3.2 方法

6.3.2.1 Handler 方法

  • 流函数处理,接收远端节点的hello消息
func (h *Handler) handleNewStream(s net.Stream) {
    defer s.Close() // nolint: errcheck

    //获取远端节点实例
    from := s.Conn().RemotePeer()

    var hello Message
    // 读取流信息到hello结构体中
    if err := cbu.NewMsgReader(s).ReadMsg(&hello); err != nil {
        log.Warningf("bad hello message from peer %s: %s", from, err)
        return
    }

    // 调用processHelloMessage方法对接收到的消息进行处理
    switch err := h.processHelloMessage(from, &hello); err {
    // 如果创世区块不一样,关闭流连接退出,不予处理
    case ErrBadGenesis:
        log.Warningf("genesis cid: %s does not match: %s, disconnecting from peer: %s", &hello.GenesisHash, h.genesis, from)
        s.Conn().Close() // nolint: errcheck
        return
    case nil: // ok, noop
    default:
        log.Error(err)
    }
}
  • 处理hello消息
func (h *Handler) processHelloMessage(from peer.ID, msg *Message) error {
    // 如果创世区块不一样,报错
    if !msg.GenesisHash.Equals(h.genesis) {
        return ErrBadGenesis
    }

    // 调用区块同步方法
    // 此回调函数实在node包实例化hello协议的时候中定义的
    h.chainSyncCB(from, msg.HeaviestTipSetCids, msg.HeaviestTipSetHeight)
    return nil
}
  • 响应远端节点的连接,回复hello消息体
func (h *Handler) getOurHelloMessage() *Message {
    heaviest := h.getHeaviestTipSet()
    height, err := heaviest.Height()
    if err != nil {
        panic("somehow heaviest tipset is empty")
    }

    return &Message{
        GenesisHash:          h.genesis,
        HeaviestTipSetCids:   heaviest.ToSortedCidSet().ToSlice(),
        HeaviestTipSetHeight: height,
    }
}

func (h *Handler) sayHello(ctx context.Context, p peer.ID) error {
    s, err := h.host.NewStream(ctx, p, protocol)
    if err != nil {
        return err
    }
    defer s.Close() // nolint: errcheck

    //获取本节点的hello消息体
    msg := h.getOurHelloMessage()

    //向远端节点发送消息体
    return cbu.NewMsgWriter(s).WriteMsg(&msg)
}

6.3.2.2 helloNotify方法

  • hello方法,返回一个handler实例
func (hn *helloNotify) hello() *Handler {
    return (*Handler)(hn)
}
  • helloNotify实现了libp2p-net/interface.go中的Notifiee接口
func (hn *helloNotify) Connected(n net.Network, c net.Conn) {
    go func() {
        ctx, cancel := context.WithTimeout(context.Background(), helloTimeout)
        defer cancel()
        p := c.RemotePeer()
        // 有其他节点连接的时候调用sayHello,发送hello消息体
        if err := hn.hello().sayHello(ctx, p); err != nil {
            log.Warningf("failed to send hello handshake to peer %s: %s", p, err)
        }
    }()
}

func (hn *helloNotify) Listen(n net.Network, a ma.Multiaddr)      {}
func (hn *helloNotify) ListenClose(n net.Network, a ma.Multiaddr) {}
func (hn *helloNotify) Disconnected(n net.Network, c net.Conn)    {}
func (hn *helloNotify) OpenedStream(n net.Network, s net.Stream)  {}
func (hn *helloNotify) ClosedStream(n net.Network, s net.Stream)  {}

6.3.3 函数

  • 创建hello实例
// New creates a new instance of the hello protocol and registers it to
// the given host, with the provided callbacks.
func New(h host.Host, gen cid.Cid, syncCallback syncCallback, getHeaviestTipSet getTipSetFunc) *Handler {
    hello := &Handler{
        host:              h,
        genesis:           gen,
        chainSyncCB:       syncCallback,
        getHeaviestTipSet: getHeaviestTipSet,
    }

    //设置流处理回调函数
    h.SetStreamHandler(protocol, hello.handleNewStream)

    //注册网络状态改变通知回调函数
    // register for connection notifications
    h.Network().Notify((*helloNotify)(hello))

    return hello
}



//上文中的helloNotify 实现了libp2p-net/interface.go中的Notifiee接口
// Notifiee is an interface for an object wishing to receive
// notifications from a Network.
type Notifiee interface {
    Listen(Network, ma.Multiaddr)      // called when network starts listening on an addr
    ListenClose(Network, ma.Multiaddr) // called when network stops listening on an addr
    Connected(Network, Conn)           // called when a connection opened
    Disconnected(Network, Conn)        // called when a connection closed
    OpenedStream(Network, Stream)      // called when a stream opened
    ClosedStream(Network, Stream)      // called when a stream closed

    // TODO
    // PeerConnected(Network, peer.ID)    // called when a peer connected
    // PeerDisconnected(Network, peer.ID) // called when a peer disconnected
}

6.3.4 实例化及业务逻辑

  • location: node/node.go

  • Node节点中定义了hello服务

type Node struct {
    ......

    HelloSvc     *hello.Handler
    ......
}
  • 启动hello服务
// Start boots up the node.
func (node *Node) Start(ctx context.Context) error {
    ......

    // Start up 'hello' handshake service
    // 定义区块同步的回调函数
    syncCallBack := func(pid libp2ppeer.ID, cids []cid.Cid, height uint64) {
        // TODO it is possible the syncer interface should be modified to
        // make use of the additional context not used here (from addr + height).
        // To keep things simple for now this info is not used.
        // 触发调用会启动同步区块的动作
        err := node.Syncer.HandleNewBlocks(context.Background(), cids)
        if err != nil {
            log.Infof("error handling blocks: %s", types.NewSortedCidSet(cids...).String())
        }
    }
    //实例化hello服务
    node.HelloSvc = hello.New(node.Host(), node.ChainReader.GenesisCid(), syncCallBack, node.ChainReader.Head)

    ......
}

[上一篇链接] filecoin技术架构分析之五:5 filecoin源码协议层分析之心跳协议
[下一篇链接] filecoin技术架构分析之七:7 filecoin源码分析之协议层存储协议

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容