libp2p 概要
概要
IPFSで使われている、ノード間で通信を行うためのプロトコル。
libp2p自体はIPFSでしか使えないというわけではなく(理論上は)どんな既存のプロトコルの上でも透過的に扱えるように設計されている。
Ethereumのp2p protocolであるdevp2pとは似てるけど別物
Python実装がEthereum FoundationのWave IVに挙がっている
特徴
サーバ・クライアントモデルのように役割固定ではなく、任意のnodeがdial(接続しにいくほう), listen(接続を待ち受けるほう)どちらも出来る
OSI参照モデルのように1レイヤー1プロトコル固定で使うのではなく、同じ役割の複数のプロトコルを同時に使うことが出来る
例えばPeer Discovery(後述)という同じ役割をするが、最初に接続するnodeはbootstrap listを、ローカルのnodeはmDNSを、他のnodeはDHT(Distribution Hash Table; 分散ハッシュテーブル)を使うなど
Peer Discover、Routing、Transportなど各コンポーネントは具体的な実装を(理論上は)プラガブルに選べる
最終的なコード(js-libp2p)
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
/protocol/addressやportなどのstring のように / で各プロトコルの情報をつなげていくイメージ
全体のアーキテクチャ
code: architecture
┌─────────────────────────────────────────────────────────────────────────────────┐
│ libp2p │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐┌─────────────────┐┌──────────────────────────┐┌───────────────┐
│ Peer Routing ││ Swarm ││ Distributed Record Store ││ Discovery │
└─────────────────┘└─────────────────┘└──────────────────────────┘└───────────────┘
Discovery
接続するpeerを見つけるためのもの
例
bootstrap node
peerを探すために最初に接続する
mDNS
ローカルマシン内の仮想環境などに接続する
kad(Kademlia)
DHT上をランダムウォークして新しいnodeに接続する
Peer Routing
例
mDNS
kad
Swarm
ストリーム(libp2p上の仮想的なコネクション)を扱う
Transport
transportに使う具体的なプロトコル(TCPなど)を扱う
現状実装があるのはほぼTCPベースのもの(TCP, WebRTC, WebSocketなど)のみ
utp(BitTorrentとかで使われているUDP上でTCPっぽい通信をするやつ)もあるらしい
複数のTransportのmultiaddrを設定しておくと、利用可能なTransportを使ってくれる
code: example
const defaults = {
modules: {
transport: [
TCP,
WebSockets
]
}
}
...
parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
], (err, nodes) => {
...
Protocol Muxing
複数の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上で使う
code: example
modules: {
...
}
streamMuxerにMplexを書いておくと、handle時のconnectionが使い回せる時は新しく作らないようになる
Distributed Record Store
DNSのようなもの
レコードの保存や配布(distribute)をする
レコードはシグナリング(signaling)、リンクの確立、peerやコンテンツの存在を知らせる等の目的で他のシステムで使われる
実際にIPFSで使われてるコンポーネント
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
感想・所感
複数プロトコルをRPCのように同時に扱えて、Transferのレイヤーを透過的に扱うというアイデアは面白い
パフォーマンスチューニングが絡んでくるとlibp2pのように疎結合にするより、Ethereumのdevp2pの実装のようにgo-ethereumなどのクライアントに直接実装して密結合にするほうが良い可能性もある?
勉強会メモ
nrryuya.icon >
分散DBのSwarmとは違うらしい
そもそもブロックチェーン文脈で開発されてたものなの?
IPFSのために作られたっぽかった