connection establish

lncli --network=simnet connect pubkey@host

の裏でどうゆう処理が走っているのか。一からたどる。 コマンドは、LNDではcliが利用される。

// connectToPeer establishes a connection to a remote peer. errChan is used to
// notify the caller if the connection attempt has failed. Otherwise, it will be
// closed.
func (s *server) connectToPeer(addr *lnwire.NetAddress, errChan chan<- error) {  
    conn, err := brontide.Dial(s.identityPriv, addr, cfg.net.Dial)

brontide.Dialは、noise protocolをサポートするスタンドアローンのlndのパッケージ。このメソッドでは、BOLT8で定義されるHandshake Exchangeが実行される。

ACT 1

-> e, es

    // Initiate the handshake by sending the first act to the receiver.
    actOne, err := b.noise.GenActOne()
    if err != nil {
        b.conn.Close()
        return nil, err
    }
    if _, err := conn.Write(actOne[:]); err != nil {
        b.conn.Close()
        return nil, err
    }

    // We'll ensure that we get ActTwo from the remote peer in a timely
    // manner. If they don't respond within 1s, then we'll kill the
    // connection.
    err = conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
    if err != nil {
        b.conn.Close()
        return nil, err
    }

一秒以内にACT 2が帰ってこない場合、エラーとする。b.noise.GenActOne()では、

    // e
    b.localEphemeral, err = b.ephemeralGen()
    if err != nil {
        return actOne, err
    }

    ephemeral := b.localEphemeral.PubKey().SerializeCompressed()
    b.mixHash(ephemeral)

    // es
    s := ecdh(b.remoteStatic, b.localEphemeral)
    b.mixKey(s[:])

    authPayload := b.EncryptAndHash([]byte{})

    actOne[0] = HandshakeVersion
    copy(actOne[1:34], ephemeral)
    copy(actOne[34:], authPayload)

としてactOneを作る。act twoをreceiveしたら、

ACT 3

-> s, se

    // Finally, complete the handshake by sending over our encrypted static
    // key and execute the final ECDH operation.
    actThree, err := b.noise.GenActThree()
    if err != nil {
        b.conn.Close()
        return nil, err
    }
    if _, err := conn.Write(actThree[:]); err != nil {
        b.conn.Close()
        return nil, err
    }

    // We'll reset the deadline as it's no longer critical beyond the
    // initial handshake.
    err = conn.SetReadDeadline(time.Time{})
    if err != nil {
        b.conn.Close()
        return nil, err
    }

これにより、ネゴシエーションが完了する。 それ以降は、ネゴシエーション時に交換した鍵情報で、BOLTのメッセージを符号化/復号化を行う。

bolt message

noise protocolによるhand shakeが終わったら、次はboltによるやり取りになる。
その際は、initからすべてが始まる。 まずは、brontide packageでhand shakeしたあとの動き。

// connectToPeer establishes a connection to a remote peer. errChan is used to
// notify the caller if the connection attempt has failed. Otherwise, it will be
// closed.
func (s *server) connectToPeer(addr *lnwire.NetAddress, errChan chan<- error) {  
    conn, err := brontide.Dial(s.identityPriv, addr, cfg.net.Dial)
    if err != nil {
        srvrLog.Errorf("Unable to connect to %v: %v", addr, err)
        select {
        case errChan <- err:
        case <-s.quit:
        }
        return
    }

    close(errChan)

    s.OutboundPeerConnected(nil, conn)
}

から、

// OutboundPeerConnected initializes a new peer in response to a new outbound
// connection.
// NOTE: This function is safe for concurrent access.
func (s *server) OutboundPeerConnected(connReq *connmgr.ConnReq, conn net.Conn) {  
    // Exit early if we have already been instructed to shutdown, this
    // prevents any delayed callbacks from accidentally registering peers.
    if s.Stopped() {
        return
    }

    nodePub := conn.(*brontide.Conn).RemotePub()
    pubStr := string(nodePub.SerializeCompressed())

でpeerを登録する。