p2p, 즉 server없는 - 요즘 유행하는 serverless 어쩌고 하는 이상한 거 말고 - peer간 통신쪽을 쭉 보고 있다.
IPFS(InterPlanetary File System)을 보면서 꽤 흥미롭다고 생각했는데 사실 매번 파일을 저장할 필요는 없다고 생각해서 IPFS를 구성하는 libp2p를 좀 살펴보기로 했다.
IPFS도 그렇지만 한글 문서는 둘째치고 영문으로도 실제로 뭔가 해본 글이 별로 없어서 본진(https://github.com/libp2p/js-libp2p/tree/master/examples)의 Examples 와 Tutorials 를 보면서 하나씩 해보고 있다.
libp2p내용은 네트웍에서 암호화까지 굉장히 많은 범위를 다루고 있으므로 어디서부터 학습을 시작할지 진입로를 찾는 것이 쉽지 않았다.
이 글에선 최대한 노이즈를 적게하면서 작은 부분 부터 하나씩 쫓아가보는 것을 목표로 한다.
peer 만들기
가장 먼저할 것은 유일하며 독립적인 peer를 만드는 일이다.
node.js 에서 가볍게 시작해보자.
mkdir p2pconn
cd p2pconn
npm init -y
npm install --save libp2p libp2p-tcp peer-info
npm install 에서 c파일들을 컴파일 하느라 약간 시간이 걸린다.
package.json을 cat package.json으로 확인해보면 대략 아래와 같을 것이다.
$ cat package.json
{
"name": "p2pconn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"libp2p": "^0.23.1",
"libp2p-tcp": "^0.13.0",
"peer-info": "^0.14.1"
}
}
test 부분을 start로 바꿔주고 index.js를 실행하게끔 변경하자.
{
"name": "p2pconn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"libp2p": "^0.23.1",
"libp2p-tcp": "^0.13.0",
"peer-info": "^0.14.1"
}
}
index.js를 생성하자.
제일 먼저 통신을 할 주체인 peer를 생성하는 것부터 시작해보자.
const PeerInfo = require('peer-info');
PeerInfo.create((err, peerInfo)=>{
console.log(peerInfo);
});
간단하다. PeerInfo 객체의 method인 create를 통해 peer의 정보를 생성하는데 npm start로 실행해보면 결과는 다음과 같다.
PeerInfo {
id:
PeerId {
_id:
<Buffer 12 20 ae df d4 ac 88 d1 ca cc f2 45 39 af b9 45 29 c7 be 74 1a cf da 05 37 ae 67 4e 3b ab 7f ba 61 74>,
_idB58String: 'Qma7Hc6rWEU9kj8cnvpjR75QDDLKy5XcnTmvUSiVJWdDF9',
_privKey: RsaPrivateKey { _key: [Object], _publicKey: [Object] },
_pubKey: undefined },
multiaddrs: MultiaddrSet { _multiaddrs: [], _observedMultiaddrs: [] },
protocols: Set {},
_connectedMultiaddr: undefined }
Process finished with exit code 0
아마 IPFS를 써본 적이 있는 분이라면 _idB58String: 'Qma7Hc6rWEU9kj8cnvpjR75QDDLKy5XcnTmvUSiVJWdDF9' 이 부분이 눈에 들어올 것이라고 생각한다.
이 값은 매번 실행할때마다 새로 생성되는 해쉬값이며 중복이 되지 않는다.
peerInfo에서 필요한 건 일단 이 식별자이다. 잘 기억해놓자.
Bundle 만들기
사실 libp2p를 하기 위해 제일 먼저 할 일은 libp2p를 상속받은 Bundle class를 만드는 일이다.
이 Bundle class의 constructor에 인자로 어떤 module들을 사용할지 지정할 수 있다.
일단 TCP 모듈을 사용해서 TCP 네트워트 안에서 자신을 식별할 수 있도록 해보자.
libp2p-tcp모듈을 사용한다. bundle.js를 만들어보자.
const libp2p = require('libp2p');
const TCP = require('libp2p-tcp');
class Bundle extends libp2p {
constructor(_options) {
const defaults = {
modules: {
transport: [
TCP
]
}
};
super({...defaults, ..._options});
}
}
module.exports = Bundle;
이런 형태가 될 것이다. 인자가 생긴 모양을 잘 보자.
modules.transport 가 array 형을 인자로 갖고 거기에 모듈인 libp2p-tcp를 끼워넣었다.
bundle을 만들었으면 이제 실제로 node instance를 생성해보자.
Node 생성
node는 node = new Bundle()로 인스턴스를 생성하면 되는데 public network에서 닿을 수 있도록 0.0.0.0을 peer정보에 지정해서 생성해본다.
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0');
를 index.js에 추가하고 bundle.js를 가져와서 peerInfo를 넣어주자.
const Bundle = require('./bundle');
const PeerInfo = require('peer-info');
console.log("peer is creating...");
PeerInfo.create((err, peerInfo)=> {
console.log("peerID is", peerInfo.id._idB58String);
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0');
const node = new Bundle({ peerInfo });
node.start(err=> {
if (err) { throw err; }
console.log(node.peerInfo.multiaddrs.toArray().map(o=>o.toString()));
});
});
index.js 를 이와 같이 수정했다.
Bundle class에 multiaddrs를 추가한 peerInfo를 가지고 node를 생성하고 node의 start function을 실행하여 정상적으로 시작한 경우 node가 가지고 있는 peerInfo의 주소들을 전부 출력하도록 했다.
아마 제대로 했다면 다음과 같은 결과를 볼 수 있을 것이다.
peer is creating...
peerID is QmXvU5Gmjy2GGotqP1CHggEffbtQ3ptbZJtxT48g2AkvLD
[ '/ip4/127.0.0.1/tcp/49170/ipfs/QmXvU5Gmjy2GGotqP1CHggEffbtQ3ptbZJtxT48g2AkvLD',
'/ip4/192.168.0.79/tcp/49170/ipfs/QmXvU5Gmjy2GGotqP1CHggEffbtQ3ptbZJtxT48g2AkvLD' ]
peerId가 같은 127.0.0.1 주소와 private IP(혹은 public IP)를 볼 수 있을 것이다. 결과는 네트웍 상황에 따라 다를 수 있다.
여러번 실행해보면 tcp 뒤에 붙는 port 와 ipfs 뒤에 붙는 peerId가 계속 바뀌는 걸 볼 수 있다.
여기까지 보다보니 multiaddrs라는게 나왔다.
peerInfo(https://github.com/libp2p/js-peer-info)도 mutliaddrs(https://github.com/multiformats/multiaddr)도 도대체 모르는 것들 투성이다.
지금 상황에서 모든 걸 다 알아보고 쫓아가기엔 조금 힘들다. 호기심을 가지고 계속 다음으로 전진하자.