Monday, December 3, 2018

Communication between Hosts in py-libp2p

Overview

Communication in py-libp2p is broken down into the notion of connections and streams. As a disclaimer, this post describes how py-libp2p is implemented at this point in time, which mostly follows the libp2p spec but may have some variations- the spec is the actual point of reference for libp2p.

Let's assume that we have two hosts (i.e. nodes) labelled A and B, and they want to communicate with each other. In order to understand how communication works, we must first understand connections and streams.

Connections

1) Raw Connection: the actual connection between two hosts, which contains a reader for reading from the connection and a writer for writing to the connection.
2) Muxed Connection: a wrapper for a raw connection that is responsible for reading/writing to the raw connection and for opening/accepting streams.

Streams

1) Stream: a specific channel for communication across a muxed connection. We can use multiple streams across the same muxed connection to communicate about different topics while still using the same underlying muxed connection.
2) Muxed Stream: an object that contains a stream ID and a reference to a muxed connection. The muxed stream is responsible for handling reads and writes for a particular stream across the muxed connection.
3) Net Stream: an object that contains a muxed stream and a protocol ID, which defines the protocol for the contents of messages sent across the given stream. 

The Goal of using Streams

The purpose of streams is to allow two hosts to have a single raw connection between each other and to communicate about multiple topics over that same raw connection. Streams provide an abstraction that allows for a new stream to be opened for each topic the two hosts wish to communicate about. We can think of the stream ID as being associated with the name of this topic. Additionally, since each stream may want to use a different protocol for communication about its topic, each stream is associated with a particular protocol. The protocol for a given stream simply tells each host how they should interpret the contents of the messages sent/received over that stream.

Example: Steak and Music

Let's say hosts A and B wish to communicate about two topics: steak recipes and music files. A and B can use stream X to request steak recipes from one another, and since steak recipes are stored as HTML files (in this example), A and B can choose to use HTTP to communicate over stream X. A and B can use stream Y to request music files from one another, and so A and B can choose to use FTP to request music files from each other over stream Y.

Walkthrough of py-libp2p Communication

Now that you know the context for streams and connections, you can grasp how they are used within py-libp2p. Here is a step-by-step walkthrough of two hosts A and B establishing a connection to each other, and then opening a stream to each other, where A is the party who asks to establish the stream with B. 


~ Create the host ~


1) Each host on creation will listen for incoming connections on one or more ports. 


~ Establish the Connection ~


2) The new stream function is called on A with an argument saying open new stream to B.
3) A opens a raw connection to B, and then A creates a muxed connection to wrap that raw connection. Then, using the muxed connection, A creates a muxed stream.
4) B's listener will see the incoming connection from A and will then call B's connection handler. In the connection handler, B will create a muxed connection from this raw connection, and will then create a muxed stream.


~ Decide on a Protocol ~


5) Now that both A and B have a muxed stream, they will engage in protocol negotiation. A will tell B which protocol(s) A would like to use for communication, and B will respond by agreeing to use the first of those protocols that B supports. If B does not support any of the protocols A suggests, B will respond to A saying that none of the suggested protocols are supported.
6) If A and B agree on the protocol to use, then A and B will each create a net stream from the muxed stream and the agreed upon protocol ID.


~ Pass Net Streams to Users ~


7) On A, the net stream is returned to the user who called the new stream function.
8) On B, the net stream is passed as an argument into a protocol handler function that is responsible for handling incoming streams on that protocol. This function is defined by the user, and so once the net stream is passed into this function, the user can do whatever he/she wants with it.


~ The End ~

9) At this point, the users can read/write to the net stream as they please on their respective host.

Monday, November 12, 2018

The Beginning of py-libp2p

Overview

The development of py-libp2p is now officially underway! As you follow us on our journey of building and designing a Python client for libp2p, it would be helpful to know who we are. We are a team of four students from the engineering school at the University of Pennsylvania. The team consists of Alex Haynes, Rob Zajac, Zixuan Zhang (ZX), and myself, Aspyn Palatnick. All of us have a passion for distributed systems and are very interested in blockchain and its various use-cases.

Progress to date

We have built a PoC of py-libp2p using the essential modules needed for basic communication. While this PoC is still a work in progress, we are currently able to send data between two py-libp2p hosts while adhering to most of the libp2p spec. As of now, the modules that we have implemented and are continuing to refine include muxed connections, swarm (implementation of the Network interface), TCP (implementation of Transport interface), streams, listeners, multiaddresses, transport upgrader, basic host, and peer store. Although all of these require more testing and further enhancements, we are very excited at the progress we have made so far and the steps that are to follow.

Feel free to check out our GitHub repo and follow our progress

What's next

Today, we officially submitted our application for an Ethereum Foundation grant. While we committed to working on py-libp2p about two months ago, we wanted to ensure that we built out the core modules and that we had some working functionality before applying for the grant. 

As we continue to work on py-libp2p, I am going to be documenting (in words) how the libp2p system works and how our implementation works. Hopefully, this will ease some of the initial confusion people have when trying to understand libp2p from both an architectural and a usage standpoint. More updates to follow!