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を実装すればいいんだな。見えた。