레이블이 P2P인 게시물을 표시합니다. 모든 게시물 표시
레이블이 P2P인 게시물을 표시합니다. 모든 게시물 표시

2019년 11월 4일 월요일

libp2p - Publish/Subscribe 를 읽고

https://docs.libp2p.io/concepts/publish-subscribe/

- Discovery

Distributed hash tables
Local network broadcasts
Exchanging peer lists with existing peers
Centralized trackers or rendezvous points
Lists of bootstrap peers

peer와의 연결에 full-message와 metadata-only 방식이 있다. full-message peering은 메시지를 주고 받을 때 사용하는 연결로서, 최대 연결 가능한 peer의 수를 제한(6정도)하여 연결을 맺는다. metadata-only peering은 full-message로 연결되어 있지 않은 peer들을 더 많이 연결을 맺어 관련 여러 정보들을 주고 받는다.(예를 들면 '난 이런 토픽들에 subscribe하고 있다', '난 이런 토픽에 unscribe 한다.' 등등)

peer는 메시지를 받으면 full-message로 연결된 peer에 메시지를 포워딩한다. 또한, 매 1초마다 metadata-only로 연결된 peer들 중 6개를 랜덤으로 골라서 최근에 받은 메시지를 보낸다. 이렇게 서로서로 받은 메시지가 무었인지를 주고 받음으로서 내가 어떤 메시지를 받았는지 못 받았는지를 확인하고 이에 따라 full-message로 연결될 peer를 조정한다.(기존에 full-message로 연결된 peer를 metadata-only로 조정하고, metadata-only로 연결된 peer를 full-message로 바꾼다던지)

내가 subscribe 하고 있지 않은 topic에도 메시지를 보낼 수 있다. 이를 Fan-out이라 한다. 

libp2p - Stream Multiplexing 을 읽고

https://docs.libp2p.io/concepts/stream-multiplexing/

Stream multiplexing: Transport 단에서 하나의 connection을 맺고 그 위로 application 단에서 필요에 따라 stream을 여러개 열어서 사용

libp2p는 peer와의 연결에 stream multiplexing을 사용한다.

Stream multiplexing을 하는 다양한 프로토콜이 있다.

: mplex, yamux, quic, spdy

libp2p - Addressing 를 읽고

https://docs.libp2p.io/concepts/addressing/

libp2p는 다른 peer와의 연결시 사용하는 프로토콜이 매우 자유롭게 설계되어 있다. 따라서, 어떤 프로토콜을 사용할지를 연결하려는 peer에게 알려주어야 한다.

/ip4/7.7.7.7/udp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

=> ip 주소는 7.7.7.7이고 udp를 사용하고 udp 포트는 4242이고 내 peer id 는 QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N 임을 알리는 multiaddr 표현

NAT 안에 있어서 릴레이가 필요한 경우는 다음과 같이 표시할 수 있다.

/ip4/7.7.7.7/tcp/4242/p2p/QmRelay/p2p-circuit/p2p/QmRelayedPeer

=> 릴레이를 해주는 peer의 id 는 QmRelay이고 이 peer의 ip 주소는 7.7.7.7이고 tcp 포트는 4242이다. 내 peer id는 QmRelayedPeer이다.

libp2p - Peer Identity 를 읽고

https://docs.libp2p.io/concepts/peer-id/

- PeerId

a cryptographic hash of a peer's public key
multihash 포맷을 사용해서 인코딩
보통 base58로 인코딩해서 표시

=> QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

multiaddrs에 표시할 때는 /p2p로 표시한다.

=> /p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

transport 주소와 같이 사용되는 경우에는 다음과 같이 된다.

/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

- PeerInfo

개별 peer에 대한 정보를 담고 있다. 나중에 특정 peer에 접속하려 할 때 사용할 수 있다.


2019년 10월 27일 일요일

libp2p - Protocols 를 읽고

https://docs.libp2p.io/concepts/protocols/

libp2p는 특정한 하나의 프로토콜을 지원하는 형태가 아니라 여러 프로토콜을 지원하고 이 중 원하는 프로토콜을 선택해서 사용할 수 있도록 하고 있습니다.

libp2p는 원하는 프로토콜을 선택하기 위해 크게 다음의 세가지 특징을 사용합니다.

- Protocol Ids

protocol negotiation에 사용하는 unique한 id(예: /my-app/amazing-protocol/1.0.0).
맨 마지막에 버전 정보가 따라옵니다.

- Handler functions

지정된 protocol id를 통한 연결 요청이 들어오는 경우 실행되는 함수
연결을 맺을 때 꼭 버전이 정확하게 맞아야만 handler function이 호출되도록 하지 않아도 됩니다. 이를 위해 match function을 사용합니다. match function이 true를 리턴하면 이에 연결된 handler function이 호출되도록 합니다.

- Binary streams

Bidirectional, reliable delivery of binary data
Supports backpressure

* Protocol Negotiation

연결을 맺고자 하는 peer는 자신이 사용하고자 하는 protocol id를 연결하고자 하는 peer에게 보냅니다. 요청을 받은 peer는 이에 대한 응답으로 연결이 가능한지 아닌지를 표현합니다.
연결을 위해 protocol id를 보낼 때 하나가 아닌 여러개를 보낼 수도 있습니다. 이 경우 요청을 받은 peer는 여러개 중 하나를 선택해서 응답하면 됩니다.

* libp2p가 사용하는 프로토콜

아래의 프로토콜들 모두 protocol buffer를 사용합니다.

- Ping

상대방이 살아있는지 아닌지를 체크합니다.
32 byte의 랜덤 데이터를 보내고 응답을 받습니다.
응답을 받으면 stream을 닫습니다.

/ipfs/ping/1.0.0

- Identify

서로서로에 대한 정보를 교환합니다.(public key, observedAddr)
(public key는 PeerId를 만들어 내는데 사용할 수 있고, observedAddr은 NAT 정보를 알아내기 위해 필요한 정보입니다.)

/ipfs/id/1.0.0

identify/push

Identify에서 약간 변형된 프로토콜로서 request가 와야만 response를 주는 Identify와는 다르게 상대 peer에게 Identify message를 보낼 수 있는 프로토콜입니다.
(relay peer인 경우 연결된 peer의 정보를 다른 peer들에 알려줄 수 있습니다.)
 
/ipfs/id/push/1.0.0

- secio

secure input/output 로서 TLS1.2에 유사한 프로토콜입니다. 현재 TLS1.3으로 변경이 작업중입니다.

/secio/1.0.0

- kad-dht

peer routing와 content routing에 사용하는 프로토콜
/ipfs/kad/1.0.0

- Circuit-Relay

peer들 사이를 relay해주는 프로토콜
/libp2p/circuit/relay/1.0.0

2019년 10월 20일 일요일

libp2p - Circuit Relay 를 읽고

https://docs.libp2p.io/concepts/circuit-relay/

Circuit Relay : 별도의 peer를 통해 통신하고자 하는 두 peer를 연결하는 transport protocol

보통 NAT 내부에 있는 peer의 경우 다른 peer와 쉽게 통신 할 수 없다. 이러한 경우 통신이 가능하도록 하기 위해 libp2p는 p2p-circuit이라는 프로토콜을 사용한다.

중간에 relay해주는 peer에게 연결은 어떻게 할 수 있을까? 기본적으로는 내가 연결이 가능한 relay peer의 주소를 peer에게 알려주면 될 것이다. 주소는 multiaddr을 사용한다.
또는 libp2p의 content routing interface를 사용해서 연결이 가능한 relay peer를 찾고 연결을 한 다음에 peer routing을 사용해서 연결한 relay peer의 주소를 다른 peer에게 알린다.(Autorelay)

2019년 10월 18일 금요일

libp2p : NAT Traversal 을 읽고

https://docs.libp2p.io/concepts/nat/

사용하는 컴퓨터가 많아지면서 IPv4에서 쓸 수 있는 IP 만으로는 전체 기기를 감당할 수가 없습니다. 그래서 NAT라고 하는 것이 나왔습니다. 내부 네트워크에서는 내부에서만 알아볼 수 있는 IP 대역(private)을 사용하고 외부로 나갈 때 외부에서 인식 가능한 IP(public)로 바꾸어서 내보내는 거지요. 외부에서 내부로 들어올 때는 public ip를 private ip로 바꾸는 변환이 일어나서 내부 기기로 데이타가 전달되게 됩니다.

NAT를 쓰는 경우에는 내부에서 외부로 나가는 건 간단하다고 할 수 있지만 외부에서 내부로 들어오는 건 쉽지 않습니다. 내부의 기기는 private ip로 연결되어 있기 때문에 외부에서 인식할 수 없기 때문이지요.

이러한 점이 p2p에서는 문제가 됩니다. route등의 설정을 바꾸다던지 해서 어떻게 외부에서 내부로 들어오도록 할 수도 있겠지만 이렇게 불편하면 아무도 p2p를 사용하지 않겠지요. 그래서 p2p에서 사용하는 여러 방법이 있습니다.

- Automatic route configuration

많은 라우터들은 UPnp나 nat-pmp같은 port forwarding 기능을 제공합니다. 따라서, libp2p는 자동으로 이러한 설정을 하는 기능을 제공합니다.

- Hole-punching(STUN)

내부에서 외부로 연결을 맺으려 하면 라우터는 이에 대한 기록(private ip <-> public ip)을 남겨서 내부와 외부가 서로 연결될 수 있도록 해줍니다. 이 경우 외부에서 이 연결고리인 public ip/port를 안다면 외부에서 내부로 연결이 가능하게 됩니다.(좀 더 정확하게는 NAT의 종류에 따라서 이것이 가능하지 않을 수도 있습니다. 참고) 그런데 보통의 경우에는 내부의 기기는 라우터를 통할 때 자신의 ip:port가 무었이 될지 알 수 없습니다. 하지만! 외부의 커넥션이 맺어진 기기는 알 수 있겠죠. 이것을 이용한 것이 STUN입니다.

내부의 기기에서 이 STUN 서버에 접속을 하면 STUN 서버는 연결이 들어온 기기의 ip와 port 정보를 응답으로 보내줍니다. 그러면 나는 내가 외부로 나갈 때의 ip와 port를 알 수 있게 되고 다른 외부의 기기들에게 '내 ip와 port는 이거야'라고 알려주면 외부의 기기들은 나한테 접속할 수 있게 되는 거죠.

libp2p의 경우는 identity protocol이라고 하는 것을 통해서 이와 유사한 기능을 구현하고 있습니다.

- AutoNAT

앞에서 말했듯이 NAT에 따라서 STUN이 제대로 동작하지 않을 수 있습니다. 그러므로 외부의 기기가 접속을 해봐서 접속이 가능한지 아닌지를 알려주면 좋을 겁니다. 이 정보를 얻은 후 접속이 잘 되는 경우에만 외부에 '내 ip와 port는 이거야'라고 알려주는 것이 좋을테니까요.

- Circuit Relay(TURN)

대상이 되는 두 기기가 직접 통신하지 않고 중간에 relay를 해주는 기기를 두고 이 기기를 통해서 통신을 하도록 하는 방법입니다.

2019년 10월 12일 토요일

libp2p: Transport 를 읽고

https://docs.libp2p.io/concepts/transport/

libp2p는 p2p 관련 프로토콜의 집합입니다.
p2p 기능을 원하는 다른 곳에서 쓰일 수 있도록 라이브러리로 되어 있습니다.

libp2p가 동작하기 위한 기본이 되는 네트워크 연결은 다양한 프로토콜을 통해 이루어 질 수 있게 되어 있습니다.(libp2p에서는 이를 transport라 부릅니다.)
이를 위해 libp2p는 TCP, WebSocket, WebRTC, QUIC등 다양한 프로토콜(참고)을 지원하고 있습니다.

개별 peer간에 연결이 이루어지려면 peer를 구별하는 이름같은 것이 있어야 할 텐데요. 이를 위해 multiaddress(참고)를 사용합니다.
간단하게 예를 들어보면 다음과 같은 형태가 됩니다.

/ip4/1.2.3.4/tcp/4321/p2p/QmcEPrat8ShnCph8WjkREzt5CPXF2RwhYxYBALDcLC1iV6

ip4 주소는 1.2.3.4 이고, tcp 포트는 4321이고, QmcEPrat...는 PeerId를 의미하는 데요. 누구에게 접속하는지를 표시하는 거라고 보면 됩니다.

PeerId(참고)를 좀 더 자세히 볼까요? peer를 유일하게 구별하는 이름인데요.
기술적으로는 public key의 해시 값입니다. 개별 peer들은 동작시 private/public key 쌍을 생성해서 유일한 값을 만들어 내고 이를 자기를 구별하는 값으로 사용합니다.

libp2p를 사용하는 애플리케이션이 하나의 transport만을 사용할 필요는 없습니다. 동시에 여러개의 transport를 지원할 수 있습니다.
이를 switch라고 합니다. switch를 통해 protocol negotiation, stream multiplexing, secure communications, connection upgrading을 할 수 있습니다.

2015년 6월 17일 수요일

BitMessage 분석

command, address version number, stream number, label, 개수, passphrase, hash값의 앞의 몇자리를 0x00으로 할 것인가, nonce trials per byte, payload length extra bytes
현재 최신 address version number는 4
nonce trials per byte는 현재 320이 최소, payload length exxtra bytes는 현재 1400이 최소

command: createChan, joinChan, createRandomAddress, createDeterministicAddress, getDeterministicAddress

address 만들기
1. 32bytes random으로 private signing key와 private encryption key를 만든다.
2. Elliptic Curve DSA 알고리즘 사용하여 public key 생성한다. 64bytes가 만들어진다.
3. 앞에서 생성된 두개(signing과 encryption)의 public key를 앞뒤로 붙인후(public signing key + public encryption key) sha512에 의한 hash를 구한다.
4. 구해진 digest를 가지고 ripemd160에 의한 hash를 구한다.
5. ripe.digest의 앞자리(1 or 2)가 0x00일 때까지 반복한다.(address의 크기를 줄이기 위한 작업)
6. address version number의 인코딩 + stream number의 인코딩 + ripe.digest
7. verify를 위해 앞의 값에 sha512를 두번 적용한 후 앞의 4bytes를 checksum으로 사용한다.
8. (6에서의 값의 hex + 7에서의 checksum의 hex) 를 integer로 변경한다.
9. integer를 base58로 인코딩한다.
10. 앞에 BitMessage를 의미하는 BM-를 덧붙인다.

양의 정수 인코딩하기
1. 253보다 작으면 big endian unsigned char
2. 253에서 65535(2^16)사이이면 (>B, 253) + >H unsigned short
3. 65536에서 2^32사이이면 (>B, 254) + >I unsigned int
4. 2^32에서 2^64사이이면 (>B, 255) + >Q unsigned long long
5. 2^64보다 큰 수는 없다.

Wallet Import Format
1. 0x80 + private key
2. sha256을 두번 적용한 hash를 구한다.
3. hash된 결과의 앞 4bytes를 checksum으로 사용한다.
4. 1의 끝에 checksum을 추가한다.
5. base58로 인코딩한다.

signature 계산
expires time + object type + address version number + stream number + tag + 0x00000001 + public signing key + public encryption key + nonce trials per byte + payload length extra bytes
 위의 내용을 private signing key로 sign한 것이 signature이다.

encrypted 계산
0x00000001 + public signing key + public encryption key + nonce trials per byte + payload length extra bytes + signature의 length + signature
위의 내용을 address의 double hash의 앞 32bytes로 암호화 한다.

실제 다른 node에 전달되는 값은 아래와 같다.
payload = POW에 의해 계산된 nonce + expires time + object type + address version number + stream number + tag + encrypted

inventoryhash: payload에 sha512를 두번 적용한 hash의 앞 32bytes
inventoryhash -> object type, stream number, payload, expires time, tag)

Generic interfaces 요점

 https://go.dev/blog/generic-interfaces  Generic interface를 정의할 때 최소한의 제약만을 정의하고 실제 구현체들이 자신만의 필요한 제약을 추가할 수 있도록 하는 것이 좋다. pointer receiver를...