peer initializationの触り

今まで、noise protocolだけを見ていたからあまり意識していなかったけど、lightningはサーバークライアントでなくて、p to pなので、それを意識しないと行けない。

これがなかなか複雑で、peer to peerの設計をまとめた本とか情報とか無いだろうか。パット見、いい本とかはなさそうだ。まあソースを読んでいくうちに有益な情報が出てくるか。webRTCとかが絡んでくるのか?

まあ一旦はlightningに特化して考えると、まずはどうやってlightning messageのやり取りが行われるのかが問題だ。

// peerConnected is a function that handles initialization a newly connected
// peer by adding it to the server's global list of all active peers, and
// starting all the goroutines the peer needs to function properly. The inbound
// boolean should be true if the peer initiated the connection to us.
func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,  
    inbound bool) {

    brontideConn := conn.(*brontide.Conn)
    addr := conn.RemoteAddr()
    pubKey := brontideConn.RemotePub()

    srvrLog.Infof("Finalizing connection to %x, inbound=%v",
        pubKey.SerializeCompressed(), inbound)

    peerAddr := &lnwire.NetAddress{
        IdentityKey: pubKey,
        Address:     addr,
        ChainNet:    activeNetParams.Net,
    }

    // With the brontide connection established, we'll now craft the local
    // feature vector to advertise to the remote node.
    localFeatures := lnwire.NewRawFeatureVector()

    // We'll signal that we understand the data loss protection feature,
    // and also that we support the new gossip query features.
    localFeatures.Set(lnwire.DataLossProtectRequired)
    localFeatures.Set(lnwire.GossipQueriesOptional)

    // Now that we've established a connection, create a peer, and it to
    // the set of currently active peers.
    p, err := newPeer(
        conn, connReq, s, peerAddr, inbound, localFeatures,
        cfg.ChanEnableTimeout,
    )
    if err != nil {
        srvrLog.Errorf("unable to create peer %v", err)
        return
    }

    // TODO(roasbeef): update IP address for link-node
    //  * also mark last-seen, do it one single transaction?

    s.addPeer(p)

    // Dispatch a goroutine to asynchronously start the peer. This process
    // includes sending and receiving Init messages, which would be a DOS
    // vector if we held the server's mutex throughout the procedure.
    s.wg.Add(1)
    go s.peerInitializer(p)
}

まあ、local featureを設定していると。

で、いよいよ飛ばす。

// sendInitMsg sends init message to remote peer which contains our currently
// supported local and global features.
func (p *peer) sendInitMsg() error {  
    msg := lnwire.NewInitMessage(
        p.server.globalFeatures.RawFeatureVector,
        p.localFeatures,
    )

    return p.writeMessage(msg)
}

lndではいろんなメッセージの定義でlnwireっていうのをみるけど、まあ通信のプロトコルっていう理解だけしとこう。

で、こんな感じでメッセージを最後には書くんだけど

    err := p.writePool.Submit(func(buf *bytes.Buffer) error {
        // Using a buffer allocated by the write pool, encode the
        // message directly into the buffer.
        _, writeErr := lnwire.WriteMessage(buf, msg, 0)
        if writeErr != nil {
            return writeErr
        }

        // Ensure the write deadline is set before we attempt to send
        // the message.
        writeDeadline := time.Now().Add(writeMessageTimeout)
        writeErr = p.conn.SetWriteDeadline(writeDeadline)
        if writeErr != nil {
            return writeErr
        }

        // Finally, write the message itself in a single swoop.
        n, writeErr = p.conn.Write(buf.Bytes())
        return writeErr
    })

write poolっていうのがあって、まあわかるようなわからないような。このへんから理解がなあ・・・

ソースをたどっていくと、基本的にpeerはpoolでマネージメントされている。要するに、peer -> pool -> nodeの順番で呼び出されている。とすると、peerの実装からか。

よし、peerを実装すればいいんだな。見えた。