Hey BlockDAG community!


Hope you’re having a great day
As a part of libP2P implementation process today is all about implementing the substreams request-response protocols in blockDAG.

Let’s understand about sub-streams

Substreams refer to a concept related to transaction processing and parallelism. Having already formed a channel and used multiplexing, the substream can be opened now. If the substream is live, the multistream-select protocol is used for the sessions to negotiate the protocol that will be used on that particular substream.
A protocol that is customary/common to just one particular chain has a <protocol-id> at its mention. The “protocol ID” that is part of the chain specification is the identifier of the protocol being used. During the installation the protocol names below have to be changed with the assigned protocol ID instead of a <protocol-id> .

Following are the standard libP2P protocols

  • /ipfs/ping/1.0.0 : At times the main channel diverts and joins a temporary minor canyon in order to contact the remote party and check if it is still switched on. In the case of a missed response, the remote will cause the player to disconnect.
  • /ipfs/id/1.0.0:  Part of our natural resource conservation efforts is to have an occasional short-lived stream where we would obtain information from remotely.
  • /<protocol_id>/kad:  We do this every now and then by using the conversion operator to open fleeting substreams of Kademlia or random walks. Each query of Kademlia searches for appropriate swarm’s substreams separately.

Algorithm

Here’s a basic implementation of libP2P protocol in blockDAG:

  1. Initialize libp2p Node
  • Set up a libp2p node instance with required configuration (e.g., peer identity, transport, host address).

Configure supported transports (e.g., TCP/IP, WebSockets) and encryption options.

2. Define Protocol ID

  • Choose a unique protocol ID for your custom protocol (e.g., /myapp/1.0.0).

This ID will be used to identify and multiplex your protocol over libp2p connections. 

3. Implement Protocol Handler

  • Define a message format for your protocol (e.g., using Protocol Buffers, JSON, or custom serialization).
  • Implement handler functions to handle incoming and outgoing protocol messages.

const myProtocolHandler = {  
// Handle incoming messages
  async handle(stream)
 {    for await (const message of stream) {

     console.log(‘Received message:’, message.toString());

   }

 },

  // Send a message

 async send(stream, message) {

   await stream.write(message);  

}    

}; 

4. Register Protocol Handler

  • Register your protocol handler with the libp2p node.
  • Specify the protocol ID and the handler functions.

await node.handle(‘/myapp/1.0.0’, async (protocol, stream) => {

 await myProtocolHandler.handle(stream); });

5. Start libp2p Node

  • Start the libp2p node and listen for incoming connections.
  • Handle incoming connections and protocol negotiations.

await node.start();

console.log(‘libp2p node started, listening on addresses:’, node.multiaddrs);

6. Establish Connections

  • Connect to other libp2p peers (e.g., using peer discovery mechanisms).
  • Initiate protocol handshakes and establish streams for your custom protocol.

const peerId = ‘…’; // ID of the peer to connect to

await node.dial(peerId);

const { stream } = await node.newStream(peerId, [‘/myapp/1.0.0’]);
 

7. Send and Receive Messages

  • Use the protocol handler to send and receive messages over the established stream.

await myProtocolHandler.send(stream, ‘Hello, libp2p!’);


8. Handle Errors and Cleanup

  • Implement error handling and cleanup logic to gracefully shut down the libp2p node.
  • Close connections and release resources.

// Gracefully stop the libp2p node

await node.stop();

Next steps!

As the putting into practice of substreams will become successful, we will carry on our way to the realization of request-response protocol in addition to notification protocol