libp2p 概要
(詳細な仕様はこちら -> libp2p spec )
概要
IPFSで使われている、ノード間で通信を行うためのプロトコル。
IPFS自体の解説はこちら -> IPFS
libp2p自体はIPFSでしか使えないというわけではなく(理論上は)どんな既存のプロトコルの上でも透過的に扱えるように設計されている。
Ethereumのp2p protocolであるdevp2pとは似てるけど別物
相互接続?しようという話は最近ある( https://github.com/ethereum/devp2p/issues/45 )
nrryuya.icon > DMM Kimさんの資料
Python実装がEthereum FoundationのWave IVに挙がっている
https://blog.ethereum.org/2018/10/15/ethereum-foundation-grants-update-wave-4/
特徴
サーバ・クライアントモデルのように役割固定ではなく、任意のnodeがdial(接続しにいくほう), listen(接続を待ち受けるほう)どちらも出来る
OSI参照モデルのように1レイヤー1プロトコル固定で使うのではなく、同じ役割の複数のプロトコルを同時に使うことが出来る
例えばPeer Discovery(後述)という同じ役割をするが、最初に接続するnodeはbootstrap listを、ローカルのnodeはmDNSを、他のnodeはDHT(Distribution Hash Table; 分散ハッシュテーブル)を使うなど
Peer Discover、Routing、Transportなど各コンポーネントは具体的な実装を(理論上は)プラガブルに選べる
最終的なコード(js-libp2p)
https://github.com/libp2p/js-libp2p#creating-your-own-libp2p-bundle
transport, streamMuxer, connEncryptionなど必要なモジュール名やconfigを指定するだけで扱えるようにするのが目的
PeerInfo
Transferのレイヤーを透過的に扱うため、各peerの情報はmultiaddrで表現される
複数のmultiaddrを持つことも出来る(=複数のTransferで通信することも出来る)
例
code: example
# IPFS over TCP over IPv6 (typical TCP)
/ip6/fe80::8823:6dff:fee7:f172/tcp/4001/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu
https://github.com/multiformats/multiaddr#specification
/protocol/addressやportなどのstring のように / で各プロトコルの情報をつなげていくイメージ
全体のアーキテクチャ
code: architecture
┌─────────────────────────────────────────────────────────────────────────────────┐
│ libp2p │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐┌─────────────────┐┌──────────────────────────┐┌───────────────┐
│ Peer Routing ││ Swarm ││ Distributed Record Store ││ Discovery │
└─────────────────┘└─────────────────┘└──────────────────────────┘└───────────────┘
Discovery
接続するpeerを見つけるためのもの
例
bootstrap node
peerを探すために最初に接続する
https://github.com/libp2p/js-libp2p/tree/master/examples/discovery-mechanisms#1-bootstrap-list-of-peers-when-booting-a-node
mDNS
ローカルマシン内の仮想環境などに接続する
https://github.com/libp2p/js-libp2p/tree/master/examples/discovery-mechanisms#2-multicastdns-to-find-other-peers-in-the-network
kad(Kademlia)
DHT上をランダムウォークして新しいnodeに接続する
https://github.com/libp2p/js-libp2p-kad-dht
kademliaのアルゴリズム: https://nazenani-torrent.firefirestyle.net/dht/kBucket.html
Peer Routing
https://github.com/libp2p/js-libp2p/tree/master/examples/transports#3-using-multiple-transports
例
mDNS
kad
https://github.com/libp2p/js-libp2p/tree/master/examples/peer-and-content-routing#1-using-peer-routing-to-find-other-peers
Swarm
ストリーム(libp2p上の仮想的なコネクション)を扱う
Transport
transportに使う具体的なプロトコル(TCPなど)を扱う
現状実装があるのはほぼTCPベースのもの(TCP, WebRTC, WebSocketなど)のみ
utp(BitTorrentとかで使われているUDP上でTCPっぽい通信をするやつ)もあるらしい
複数のTransportのmultiaddrを設定しておくと、利用可能なTransportを使ってくれる
TCP+WebSocketの例
code: example
const defaults = {
modules: {
transport: [
TCP,
WebSockets
]
}
}
...
parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws', cb),
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
], (err, nodes) => {
...
Protocol Muxing
https://github.com/libp2p/js-libp2p/tree/master/examples/protocol-and-stream-muxing#1-handle-multiple-protocols
複数のprotocolをhandleすることが出来る
code: example
node.handle('/protocol1', (protocol, conn) => {
...
}
node.handle('/protocol2', (protocol, conn) => {
...
}
semantic versioningを使える
code: example
node.handle('/another-protocol/1.0.1', (protocol, conn) => {
RPC/REST API風のcustom matcherを作れる
code: example
node.handle('/custom-match-func', (protocol, conn) => {
...
}, (myProtocol, requestedProtocol, callback) => {
if (myProtocol.indexOf(requestedProtocol)) {
callback(null, true)
} else {
callback(null, false)
}
})
Connection
Stream Muxing
複数のstreamを同じconnection上で使う
js-libp2p-mplex
code: example
modules: {
transport: TCP ,
streamMuxer: Mplex ,
...
}
streamMuxerにMplexを書いておくと、handle時のconnectionが使い回せる時は新しく作らないようになる
js-libp2p-spdyというのもあったけどspdyが無くなったので実質mplexしか使われて無さそう
Distributed Record Store
DNSのようなもの
レコードの保存や配布(distribute)をする
レコードはシグナリング(signaling)、リンクの確立、peerやコンテンツの存在を知らせる等の目的で他のシステムで使われる
実際にIPFSで使われてるコンポーネント
https://github.com/ipfs/js-ipfs/blob/a6226680fe2a9a2200514af8c91d1d8e3ba612b2/package.json#L129-L143
code: components
"libp2p": "~0.23.0",
"libp2p-bootstrap": "~0.9.3",
"libp2p-circuit": "~0.2.0",
"libp2p-crypto": "~0.13.0",
"libp2p-floodsub": "~0.15.0",
"libp2p-kad-dht": "~0.10.1",
"libp2p-keychain": "~0.3.1",
"libp2p-mdns": "~0.12.0",
"libp2p-mplex": "~0.8.0",
"libp2p-record": "~0.5.1",
"libp2p-secio": "~0.10.0",
"libp2p-tcp": "~0.12.0",
"libp2p-webrtc-star": "~0.15.3",
"libp2p-websocket-star": "~0.8.1",
"libp2p-websockets": "~0.12.0",
その他調べられてないやつ
NAT traversal
NAT越え
Relay
NAT越え出来なかった時の別経路探索
Encryption
https://github.com/libp2p/js-libp2p-secio
感想・所感
複数プロトコルをRPCのように同時に扱えて、Transferのレイヤーを透過的に扱うというアイデアは面白い
現在のlibp2pの仕様だとUDPのようなコネクションレスな通信が実装出来ないという問題 ( https://github.com/libp2p/go-udp-transport/issues/3 )があり、実質TCP-likeなものしか動いてないのでTransportで実装出来る範囲が限られてる
パフォーマンスチューニングが絡んでくるとlibp2pのように疎結合にするより、Ethereumのdevp2pの実装のようにgo-ethereumなどのクライアントに直接実装して密結合にするほうが良い可能性もある?
勉強会メモ
nrryuya.icon >
分散DBのSwarmとは違うらしい
そもそもブロックチェーン文脈で開発されてたものなの?
IPFSのために作られたっぽかった
yudetamago.icon > IPFS作るときにこういうプロトコル・実装必要だよねってなって出来たっぽい ( https://github.com/libp2p/specs/blob/master/1-introduction.md#1-introduction )
#P2P #Network #IPFS