2 Initial library design roadmap
tomaka edited this page 2017-11-28 12:03:58 +01:00

The various concepts of libp2p:

  • The Transport trait provides the dial and listen_on methods. Its purpose is to open connections to nodes by passing their multiaddresses. Implemented on things such as Tcp, Websockets, or EitherTransport<A, B>.

  • The ConnectionUpgrade<S: AsyncRead + AsyncWrite> trait has an upgrade method that will turn an S into an Output (associated type of ConnectionUpgrade). Also contains methods that will determine whether a remote supports this upgrade. Implemented on Secio, Multiplex, etc.

  • The StreamMuxer trait is implemented on objects that can be turned into either an incoming substream or an outgoing substream. Creating a substream (either incoming or outgoing) consumes the whole object, but the user can create multiple substreams by cloning it beforehand. As an example, the Output associated type of Multiplex and Yamux implements StreamMuxer, and can be cloned because it contains an Arc. For convenience, StreamMuxer is also automatically implemented on any object that also implements AsyncRead and AsyncWrite and the object will be considered as a single substream.

  • If the Output of a ConnectionUpgrade implements StreamMuxer, then a transport wrapped inside a connection upgrade will also implement Transport. In practice this means that for example the type UpgradedNode<Tcp, Secio> also implements Transport. If for example you want to dial a node and apply secio over it, call dial on that UpgradedNode. Advanced features, such as adding new transports at runtime, or conditionnally enabling upgrades, can be done by combining modifiers.

  • In order to actually perform muxing, the user has to wrap the UpgradedNode into a ConnectionReuse struct (note: name could change) A ConnectionReuse object implements Transport and will keep and clone existing connections instead of opening new ones. It is likely that this object can only be used on top of an UpgradedNode with muxing, but the only restriction is that the connection must be clonable, and it is not impossible that this mechanism could serve for other purposes (unit testing comes to mind).

  • Intermediate layers such as secio or multiplex implement ConnectionUpgrade, and have an Output type that implements StreamMuxer, so that other additional upgrades can be built on top of them. On the other hand concrete non-intermediary-layer protocol implementations (such as ping, identify or p2p-circuit) also implement ConnectionUpgrade, but have an Output that doesn't implement any specific trait. Therefore UpgradedNode<..., SomeProtocol> will not implement Transport. Instead the meaning of the Output type for protocols is protocol-specific.

  • The multiplex intermediate layer can be considered as an optimization when applied over a Transport. Whenever you attempt to dial using a transport upgraded with multiplex, the muxer will attempt to spawn a new stream on an existing connection instead of actually dialing the node.

  • The p2p-circuit protocol (ie. using nodes as proxy) takes the form of a P2pCircuitTransport<T> struct where T is a Transport stored within the P2pCircuitTransport. This struct will apply some upgrades on this T, and itself implements the Transport trait. In practice, the user will generally create a stack of transports, then clone that stack and build a P2pCircuitTransport with it, then join the stack and the P2pCircuitTransport together into one final Transport.

  • In parallel to the Transport and ConnectionUpgrade traits, the Peerstore trait allows storing information about peers (by their PeerId), and the Recordstore trait allows storing general-purpose key-value data. Implementations of these two traits use interior mutability, and try to be as lock-free as possible, in order to be sharable by Arc.

  • Initializing a swarm of nodes that uses a Kademlia DHT is done by calling a function that sits on top of the Transport and ConnectionUpgrade system described above. Amongst several things, you must pass to this function an implementation of Transport, an implementation of Peerstore and an implementation of Recordstore. The Transport must be clonable and will be used to listen and to dial nodes whenever Kademlia requires it. The Transport should include the wanted intermediary upgrades, such as secio and multiplex.

    Whenever we need to dial a node, the Kademlia DHT implementation will automatically dial the node three times (note: remember that thanks to multiplex this only results in one connection) and opens the identify, ping and kademlia protocol upgrades on the connections it receives and dials. Each of these connection upgrades has a different Output, but the Kademlia-building function returns an object that provides a unified API that sits on top of these outputs.

  • The objects passed to the Kademlia DHT creation function (transport, peer store and record store) are all meant to be clonable for a cheap cost (eg. by using Arcs), so that the user can create a Kademlia DHT in parallel to another kind of DHT, or protocol (eg. all the IPFS protocols), if they wish to do so.

  • The peerstore object is generally (but not always) shared by Arc throughout the various parts of the architecture that need information about peers. This means that core operations such as pinging, discovering and identifying nodes are not redundant amongst the various parts.

  • In order to create a swarm of nodes, some bootstrapping is required. This is done by adding known nodes to the peer store before calling the Kademlia-building function.