Chat Demo
This example demonstrates how to create a simple chat application using libp2p.
$ python -m pip install libp2p
Collecting libp2p
...
Successfully installed libp2p-x.x.x
$ chat-demo
Run this from the same folder in another console:
chat-demo -p 8001 -d /ip4/127.0.0.1/tcp/8000/p2p/QmPouApKqyxJDy6YT21EXNS6efuNzvJ3W3kqRQxkQ77GFJ
Waiting for incoming connection...
Copy the line that starts with chat-demo -p 8001, open a new terminal in the same
folder and paste it in:
$ chat-demo -p 8001 -d /ip4/127.0.0.1/tcp/8000/p2p/QmPouApKqyxJDy6YT21EXNS6efuNzvJ3W3kqRQxkQ77GFJ
Connected to peer /ip4/127.0.0.1/tcp/8000
You can then start typing messages in either terminal and see them relayed to the
other terminal. To exit the demo, send a keyboard interrupt (Ctrl+C) in either terminal.
The full source code for this example is below:
1import argparse
2import logging
3import sys
4
5import multiaddr
6import trio
7
8from libp2p import (
9 new_host,
10)
11from libp2p.custom_types import (
12 TProtocol,
13)
14from libp2p.network.stream.net_stream import (
15 INetStream,
16)
17from libp2p.peer.peerinfo import (
18 info_from_p2p_addr,
19)
20
21# Configure minimal logging
22logging.basicConfig(level=logging.WARNING)
23logging.getLogger("multiaddr").setLevel(logging.WARNING)
24logging.getLogger("libp2p").setLevel(logging.WARNING)
25
26PROTOCOL_ID = TProtocol("/chat/1.0.0")
27MAX_READ_LEN = 2**32 - 1
28
29
30async def read_data(stream: INetStream) -> None:
31 while True:
32 read_bytes = await stream.read(MAX_READ_LEN)
33 if read_bytes is not None:
34 read_string = read_bytes.decode()
35 if read_string != "\n":
36 # Green console colour: \x1b[32m
37 # Reset console colour: \x1b[0m
38 print("\x1b[32m %s\x1b[0m " % read_string, end="")
39
40
41async def write_data(stream: INetStream) -> None:
42 async_f = trio.wrap_file(sys.stdin)
43 while True:
44 line = await async_f.readline()
45 await stream.write(line.encode())
46
47
48async def run(port: int, destination: str) -> None:
49 from libp2p.utils.address_validation import (
50 find_free_port,
51 get_available_interfaces,
52 get_optimal_binding_address,
53 )
54
55 if port <= 0:
56 port = find_free_port()
57
58 listen_addrs = get_available_interfaces(port)
59 host = new_host()
60 async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery:
61 # Start the peer-store cleanup task
62 nursery.start_soon(host.get_peerstore().start_cleanup_task, 60)
63
64 if not destination: # its the server
65
66 async def stream_handler(stream: INetStream) -> None:
67 nursery.start_soon(read_data, stream)
68 nursery.start_soon(write_data, stream)
69
70 host.set_stream_handler(PROTOCOL_ID, stream_handler)
71
72 # Get all available addresses with peer ID
73 all_addrs = host.get_addrs()
74
75 print("Listener ready, listening on:\n")
76 for addr in all_addrs:
77 print(f"{addr}")
78
79 # Use optimal address for the client command
80 optimal_addr = get_optimal_binding_address(port)
81 optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}"
82 print(
83 f"\nRun this from the same folder in another console:\n\n"
84 f"chat-demo -d {optimal_addr_with_peer}\n"
85 )
86 print("Waiting for incoming connection...")
87
88 else: # its the client
89 maddr = multiaddr.Multiaddr(destination)
90 info = info_from_p2p_addr(maddr)
91 # Associate the peer with local ip address
92 await host.connect(info)
93 # Start a stream with the destination.
94 # Multiaddress of the destination peer is fetched from the peerstore
95 # using 'peerId'.
96 stream = await host.new_stream(info.peer_id, [PROTOCOL_ID])
97
98 nursery.start_soon(read_data, stream)
99 nursery.start_soon(write_data, stream)
100 print(f"Connected to peer {info.addrs[0]}")
101
102 await trio.sleep_forever()
103
104
105def main() -> None:
106 description = """
107 This program demonstrates a simple p2p chat application using libp2p.
108 To use it, first run 'python ./chat -p <PORT>', where <PORT> is the port number.
109 Then, run another host with 'python ./chat -p <ANOTHER_PORT> -d <DESTINATION>',
110 where <DESTINATION> is the multiaddress of the previous listener host.
111 """
112 example_maddr = (
113 "/ip4/[HOST_IP]/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q"
114 )
115 parser = argparse.ArgumentParser(description=description)
116 parser.add_argument("-p", "--port", default=0, type=int, help="source port number")
117 parser.add_argument(
118 "-d",
119 "--destination",
120 type=str,
121 help=f"destination multiaddr string, e.g. {example_maddr}",
122 )
123 args = parser.parse_args()
124
125 try:
126 trio.run(run, *(args.port, args.destination))
127 except KeyboardInterrupt:
128 pass
129
130
131if __name__ == "__main__":
132 main()