Identify Push Protocol Demo
This example demonstrates how to use the libp2p identify-push protocol, which allows nodes to proactively push their identity information to peers when it changes.
$ python -m pip install libp2p
Collecting libp2p
...
Successfully installed libp2p-x.x.x
$ identify-push-demo
==== Starting Identify-Push Example ====
Host 1 listening on /ip4/127.0.0.1/tcp/xxxxx/p2p/QmAbCdEfGhIjKlMnOpQrStUvWxYz
Peer ID: QmAbCdEfGhIjKlMnOpQrStUvWxYz
Host 2 listening on /ip4/127.0.0.1/tcp/xxxxx/p2p/QmZyXwVuTaBcDeRsSkJpOpWrSt
Peer ID: QmZyXwVuTaBcDeRsSkJpOpWrSt
Connecting Host 2 to Host 1...
Host 2 successfully connected to Host 1
Host 1 pushing identify information to Host 2...
Identify push completed successfully!
Example completed successfully!
There is also a more interactive version of the example which runs as separate listener and dialer processes:
$ identify-push-listener-dialer-demo
==== Starting Identify-Push Listener on port 8888 ====
Listener host ready!
Listening on: /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM
Peer ID: QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM
Run dialer with command:
identify-push-listener-dialer-demo -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM
Waiting for incoming connections... (Ctrl+C to exit)
Copy the line that starts with identify-push-listener-dialer-demo -d ..., open a new terminal in the same
folder and paste it in:
$ identify-push-listener-dialer-demo -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM
==== Starting Identify-Push Dialer on port 8889 ====
Dialer host ready!
Listening on: /ip4/127.0.0.1/tcp/8889/p2p/QmZyXwVuTaBcDeRsSkJpOpWrSt
Connecting to peer: QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM
Successfully connected to listener!
Pushing identify information to listener...
Identify push completed successfully!
Example completed successfully!
The identify-push protocol enables libp2p nodes to proactively notify their peers when their metadata changes, such as supported protocols or listening addresses. This helps maintain an up-to-date view of the network without requiring regular polling.
The full source code for these examples is below:
Basic example:
1#!/usr/bin/env python3
2"""
3Example demonstrating the identify/push protocol.
4
5This example shows how to:
61. Set up a host with the identify/push protocol handler
72. Connect to another peer
83. Push identify information to the peer
94. Receive and process identify/push messages
10"""
11
12import logging
13
14import multiaddr
15import trio
16
17from libp2p import (
18 new_host,
19)
20from libp2p.abc import (
21 INetStream,
22)
23from libp2p.crypto.secp256k1 import (
24 create_new_key_pair,
25)
26from libp2p.custom_types import (
27 TProtocol,
28)
29from libp2p.identity.identify.pb.identify_pb2 import (
30 Identify,
31)
32from libp2p.identity.identify_push import (
33 ID_PUSH,
34 push_identify_to_peer,
35)
36from libp2p.peer.peerinfo import (
37 info_from_p2p_addr,
38)
39from libp2p.utils.address_validation import (
40 get_available_interfaces,
41)
42
43# Configure logging
44logger = logging.getLogger(__name__)
45
46
47def create_custom_identify_handler(host, host_name: str):
48 """Create a custom identify handler that displays received information."""
49
50 async def handle_identify(stream: INetStream) -> None:
51 peer_id = stream.muxed_conn.peer_id
52 print(f"\n🔍 {host_name} received identify request from peer: {peer_id}")
53
54 # Get the standard identify response using the existing function
55 from libp2p.identity.identify.identify import (
56 _mk_identify_protobuf,
57 _remote_address_to_multiaddr,
58 )
59
60 # Get observed address
61 observed_multiaddr = None
62 try:
63 remote_address = stream.get_remote_address()
64 if remote_address:
65 observed_multiaddr = _remote_address_to_multiaddr(remote_address)
66 except Exception:
67 pass
68
69 # Build the identify protobuf
70 identify_msg = _mk_identify_protobuf(host, observed_multiaddr)
71 response_data = identify_msg.SerializeToString()
72
73 print(f" 📋 {host_name} identify information:")
74 if identify_msg.HasField("protocol_version"):
75 print(f" Protocol Version: {identify_msg.protocol_version}")
76 if identify_msg.HasField("agent_version"):
77 print(f" Agent Version: {identify_msg.agent_version}")
78 if identify_msg.HasField("public_key"):
79 print(f" Public Key: {identify_msg.public_key.hex()[:16]}...")
80 if identify_msg.listen_addrs:
81 print(" Listen Addresses:")
82 for addr_bytes in identify_msg.listen_addrs:
83 addr = multiaddr.Multiaddr(addr_bytes)
84 print(f" - {addr}")
85 if identify_msg.protocols:
86 print(" Supported Protocols:")
87 for protocol in identify_msg.protocols:
88 print(f" - {protocol}")
89
90 # Send the response
91 await stream.write(response_data)
92 await stream.close()
93
94 return handle_identify
95
96
97def create_custom_identify_push_handler(host, host_name: str):
98 """Create a custom identify/push handler that displays received information."""
99
100 async def handle_identify_push(stream: INetStream) -> None:
101 peer_id = stream.muxed_conn.peer_id
102 print(f"\n📤 {host_name} received identify/push from peer: {peer_id}")
103
104 try:
105 # Read the identify message using the utility function
106 from libp2p.utils.varint import read_length_prefixed_protobuf
107
108 data = await read_length_prefixed_protobuf(stream, use_varint_format=True)
109
110 # Parse the identify message
111 identify_msg = Identify()
112 identify_msg.ParseFromString(data)
113
114 print(" 📋 Received identify information:")
115 if identify_msg.HasField("protocol_version"):
116 print(f" Protocol Version: {identify_msg.protocol_version}")
117 if identify_msg.HasField("agent_version"):
118 print(f" Agent Version: {identify_msg.agent_version}")
119 if identify_msg.HasField("public_key"):
120 print(f" Public Key: {identify_msg.public_key.hex()[:16]}...")
121 if identify_msg.HasField("observed_addr") and identify_msg.observed_addr:
122 observed_addr = multiaddr.Multiaddr(identify_msg.observed_addr)
123 print(f" Observed Address: {observed_addr}")
124 if identify_msg.listen_addrs:
125 print(" Listen Addresses:")
126 for addr_bytes in identify_msg.listen_addrs:
127 addr = multiaddr.Multiaddr(addr_bytes)
128 print(f" - {addr}")
129 if identify_msg.protocols:
130 print(" Supported Protocols:")
131 for protocol in identify_msg.protocols:
132 print(f" - {protocol}")
133
134 # Update the peerstore with the new information
135 from libp2p.identity.identify_push.identify_push import (
136 _update_peerstore_from_identify,
137 )
138
139 await _update_peerstore_from_identify(
140 host.get_peerstore(), peer_id, identify_msg
141 )
142
143 print(f" ✅ {host_name} updated peerstore with new information")
144
145 except Exception as e:
146 print(f" ❌ Error processing identify/push: {e}")
147 finally:
148 await stream.close()
149
150 return handle_identify_push
151
152
153async def display_peerstore_info(host, host_name: str, peer_id, description: str):
154 """Display peerstore information for a specific peer."""
155 peerstore = host.get_peerstore()
156
157 try:
158 addrs = peerstore.addrs(peer_id)
159 except Exception:
160 addrs = []
161
162 try:
163 protocols = peerstore.get_protocols(peer_id)
164 except Exception:
165 protocols = []
166
167 print(f"\n📚 {host_name} peerstore for {description}:")
168 print(f" Peer ID: {peer_id}")
169 if addrs:
170 print(" Addresses:")
171 for addr in addrs:
172 print(f" - {addr}")
173 else:
174 print(" Addresses: None")
175
176 if protocols:
177 print(" Protocols:")
178 for protocol in protocols:
179 print(f" - {protocol}")
180 else:
181 print(" Protocols: None")
182
183
184async def main() -> None:
185 print("\n==== Starting Enhanced Identify-Push Example ====\n")
186
187 # Create key pairs for the two hosts
188 key_pair_1 = create_new_key_pair()
189 key_pair_2 = create_new_key_pair()
190
191 # Create the first host
192 host_1 = new_host(key_pair=key_pair_1)
193
194 # Set up custom identify and identify/push handlers
195 host_1.set_stream_handler(
196 TProtocol("/ipfs/id/1.0.0"), create_custom_identify_handler(host_1, "Host 1")
197 )
198 host_1.set_stream_handler(
199 ID_PUSH, create_custom_identify_push_handler(host_1, "Host 1")
200 )
201
202 # Create the second host
203 host_2 = new_host(key_pair=key_pair_2)
204
205 # Set up custom identify and identify/push handlers
206 host_2.set_stream_handler(
207 TProtocol("/ipfs/id/1.0.0"), create_custom_identify_handler(host_2, "Host 2")
208 )
209 host_2.set_stream_handler(
210 ID_PUSH, create_custom_identify_push_handler(host_2, "Host 2")
211 )
212
213 # Start listening on available interfaces using random ports
214 listen_addrs_1 = get_available_interfaces(0) # 0 for random port
215 listen_addrs_2 = get_available_interfaces(0) # 0 for random port
216
217 async with (
218 host_1.run(listen_addrs_1),
219 host_2.run(listen_addrs_2),
220 trio.open_nursery() as nursery,
221 ):
222 # Start the peer-store cleanup task
223 nursery.start_soon(host_1.get_peerstore().start_cleanup_task, 60)
224 nursery.start_soon(host_2.get_peerstore().start_cleanup_task, 60)
225
226 # Get the addresses of both hosts
227 addr_1 = host_1.get_addrs()[0]
228 addr_2 = host_2.get_addrs()[0]
229
230 print("🏠 Host Configuration:")
231 print(f" Host 1: {addr_1}")
232 print(f" Host 1 Peer ID: {host_1.get_id().pretty()}")
233 print(f" Host 2: {addr_2}")
234 print(f" Host 2 Peer ID: {host_2.get_id().pretty()}")
235
236 print("\n🔗 Connecting Host 2 to Host 1...")
237
238 # Connect host_2 to host_1
239 peer_info = info_from_p2p_addr(addr_1)
240 await host_2.connect(peer_info)
241 print("✅ Host 2 successfully connected to Host 1")
242
243 # Run the identify protocol from host_2 to host_1
244 print("\n🔄 Running identify protocol (Host 2 → Host 1)...")
245 from libp2p.identity.identify.identify import ID as IDENTIFY_PROTOCOL_ID
246
247 stream = await host_2.new_stream(host_1.get_id(), (IDENTIFY_PROTOCOL_ID,))
248 response = await stream.read()
249 await stream.close()
250
251 # Run the identify protocol from host_1 to host_2
252 print("\n🔄 Running identify protocol (Host 1 → Host 2)...")
253 stream = await host_1.new_stream(host_2.get_id(), (IDENTIFY_PROTOCOL_ID,))
254 response = await stream.read()
255 await stream.close()
256
257 # Update Host 1's peerstore with Host 2's addresses
258 identify_msg = Identify()
259 identify_msg.ParseFromString(response)
260 peerstore_1 = host_1.get_peerstore()
261 peer_id_2 = host_2.get_id()
262 for addr_bytes in identify_msg.listen_addrs:
263 maddr = multiaddr.Multiaddr(addr_bytes)
264 peerstore_1.add_addr(peer_id_2, maddr, ttl=3600)
265
266 # Display peerstore information before push
267 await display_peerstore_info(
268 host_1, "Host 1", peer_id_2, "Host 2 (before push)"
269 )
270
271 # Push identify information from host_1 to host_2
272 print("\n📤 Host 1 pushing identify information to Host 2...")
273
274 try:
275 success = await push_identify_to_peer(host_1, host_2.get_id())
276
277 if success:
278 print("✅ Identify push completed successfully!")
279 else:
280 print("⚠️ Identify push didn't complete successfully")
281
282 except Exception as e:
283 print(f"❌ Error during identify push: {str(e)}")
284
285 # Give a moment for the identify/push processing to complete
286 await trio.sleep(0.5)
287
288 # Display peerstore information after push
289 await display_peerstore_info(host_1, "Host 1", peer_id_2, "Host 2 (after push)")
290 await display_peerstore_info(
291 host_2, "Host 2", host_1.get_id(), "Host 1 (after push)"
292 )
293
294 # Give more time for background tasks to finish and connections to stabilize
295 print("\n⏳ Waiting for background tasks to complete...")
296 await trio.sleep(1.0)
297
298 # Gracefully close connections to prevent connection errors
299 print("🔌 Closing connections...")
300 await host_2.disconnect(host_1.get_id())
301 await trio.sleep(0.2)
302
303 print("\n🎉 Example completed successfully!")
304
305
306if __name__ == "__main__":
307 trio.run(main)
308
309
310def run_main():
311 """Non-async entry point for the console script."""
312 trio.run(main)
Listener/Dialer example:
1#!/usr/bin/env python3
2"""
3Example demonstrating the identify/push protocol with separate listener and dialer
4roles.
5
6This example shows how to:
71. Set up a listener host with the identify/push protocol handler
82. Connect to the listener from a dialer peer
93. Push identify information to the listener
104. Receive and process identify/push messages
11
12Usage:
13 # First run this script as a listener (default port 8888):
14 python identify_push_listener_dialer.py
15
16 # Then in another console, run as a dialer (default port 8889):
17 python identify_push_listener_dialer.py -d /ip4/[HOST_IP]/tcp/8888/p2p/PEER_ID
18 (where PEER_ID is the peer ID displayed by the listener)
19"""
20
21import argparse
22import logging
23import sys
24
25import multiaddr
26from multiaddr import (
27 Multiaddr,
28)
29import trio
30
31from libp2p import (
32 new_host,
33)
34from libp2p.abc import (
35 INetStream,
36)
37from libp2p.crypto.secp256k1 import (
38 create_new_key_pair,
39)
40from libp2p.identity.identify import (
41 ID as ID_IDENTIFY,
42 identify_handler_for,
43)
44from libp2p.identity.identify.identify import (
45 _remote_address_to_multiaddr,
46)
47from libp2p.identity.identify.pb.identify_pb2 import (
48 Identify,
49)
50from libp2p.identity.identify_push import (
51 ID_PUSH as ID_IDENTIFY_PUSH,
52 identify_push_handler_for,
53 push_identify_to_peer,
54)
55from libp2p.peer.peerinfo import (
56 info_from_p2p_addr,
57)
58
59# Configure minimal logging
60logging.basicConfig(level=logging.WARNING)
61logging.getLogger("multiaddr").setLevel(logging.WARNING)
62logging.getLogger("libp2p").setLevel(logging.WARNING)
63
64# Configure logging
65logger = logging.getLogger("libp2p.identity.identify-push-example")
66
67
68def custom_identify_push_handler_for(host, use_varint_format: bool = True):
69 """
70 Create a custom handler for the identify/push protocol that logs and prints
71 the identity information received from the dialer.
72
73 Args:
74 host: The libp2p host
75 use_varint_format: If True, expect length-prefixed format; if False, expect
76 raw protobuf
77
78 """
79
80 async def handle_identify_push(stream: INetStream) -> None:
81 peer_id = stream.muxed_conn.peer_id
82
83 # Get remote address information
84 try:
85 remote_address = stream.get_remote_address()
86 if remote_address:
87 observed_multiaddr = _remote_address_to_multiaddr(remote_address)
88 logger.info(
89 "Connection from remote peer %s, address: %s, multiaddr: %s",
90 peer_id,
91 remote_address,
92 observed_multiaddr,
93 )
94 print(f"\n🔗 Received identify/push request from peer: {peer_id}")
95 # Add the peer ID to create a complete multiaddr
96 complete_multiaddr = f"{observed_multiaddr}/p2p/{peer_id}"
97 print(f" Remote address: {complete_multiaddr}")
98 except Exception as e:
99 logger.error("Error getting remote address: %s", e)
100 print(f"\n🔗 Received identify/push request from peer: {peer_id}")
101
102 try:
103 # Use the utility function to read the protobuf message
104 from libp2p.utils.varint import read_length_prefixed_protobuf
105
106 data = await read_length_prefixed_protobuf(stream, use_varint_format)
107
108 identify_msg = Identify()
109 identify_msg.ParseFromString(data)
110
111 # Log and print the identify information
112 logger.info("Received identify/push from peer %s", peer_id)
113 print(f"\n==== Received identify/push from peer {peer_id} ====")
114
115 if identify_msg.HasField("protocol_version"):
116 logger.info(" Protocol Version: %s", identify_msg.protocol_version)
117 print(f" Protocol Version: {identify_msg.protocol_version}")
118
119 if identify_msg.HasField("agent_version"):
120 logger.info(" Agent Version: %s", identify_msg.agent_version)
121 print(f" Agent Version: {identify_msg.agent_version}")
122
123 if identify_msg.HasField("public_key"):
124 logger.info(
125 " Public Key: %s", identify_msg.public_key.hex()[:16] + "..."
126 )
127 print(f" Public Key: {identify_msg.public_key.hex()[:16]}...")
128
129 if identify_msg.listen_addrs:
130 addrs = [Multiaddr(addr) for addr in identify_msg.listen_addrs]
131 logger.info(" Listen Addresses: %s", addrs)
132 print(" Listen Addresses:")
133 for addr in addrs:
134 print(f" - {addr}")
135
136 if identify_msg.HasField("observed_addr") and identify_msg.observed_addr:
137 observed_addr = Multiaddr(identify_msg.observed_addr)
138 logger.info(" Observed Address: %s", observed_addr)
139 print(f" Observed Address: {observed_addr}")
140
141 if identify_msg.protocols:
142 logger.info(" Protocols: %s", identify_msg.protocols)
143 print(" Protocols:")
144 for protocol in identify_msg.protocols:
145 print(f" - {protocol}")
146
147 # Update the peerstore with the new information as usual
148 peerstore = host.get_peerstore()
149 from libp2p.identity.identify_push.identify_push import (
150 _update_peerstore_from_identify,
151 )
152
153 await _update_peerstore_from_identify(peerstore, peer_id, identify_msg)
154
155 logger.info("Successfully processed identify/push from peer %s", peer_id)
156 print(f"✅ Successfully processed identify/push from peer {peer_id}")
157
158 except Exception as e:
159 error_msg = str(e)
160 logger.error(
161 "Error processing identify/push from %s: %s", peer_id, error_msg
162 )
163 print(f"\nError processing identify/push from {peer_id}: {error_msg}")
164
165 # Check for specific format mismatch errors
166 if (
167 "Error parsing message" in error_msg
168 or "DecodeError" in error_msg
169 or "ParseFromString" in error_msg
170 ):
171 print("\n" + "=" * 60)
172 print("FORMAT MISMATCH DETECTED!")
173 print("=" * 60)
174 if use_varint_format:
175 print(
176 "You are using length-prefixed format (default) but the "
177 "dialer is using raw protobuf format."
178 )
179 print("\nTo fix this, run the dialer with the --raw-format flag:")
180 print(
181 "identify-push-listener-dialer-demo --raw-format -d <ADDRESS>"
182 )
183 else:
184 print("You are using raw protobuf format but the dialer")
185 print("is using length-prefixed format (default).")
186 print(
187 "\nTo fix this, run the dialer without the --raw-format flag:"
188 )
189 print("identify-push-listener-dialer-demo -d <ADDRESS>")
190 print("=" * 60)
191 finally:
192 # Close the stream after processing
193 await stream.close()
194
195 return handle_identify_push
196
197
198async def run_listener(
199 port: int, use_varint_format: bool = True, raw_format_flag: bool = False
200) -> None:
201 """Run a host in listener mode."""
202 from libp2p.utils.address_validation import find_free_port, get_available_interfaces
203
204 if port <= 0:
205 port = find_free_port()
206
207 format_name = "length-prefixed" if use_varint_format else "raw protobuf"
208 print(
209 f"\n==== Starting Identify-Push Listener on port {port} "
210 f"(using {format_name} format) ====\n"
211 )
212
213 # Create key pair for the listener
214 key_pair = create_new_key_pair()
215
216 # Create the listener host
217 host = new_host(key_pair=key_pair)
218
219 # Set up the identify and identify/push handlers with specified format
220 host.set_stream_handler(
221 ID_IDENTIFY, identify_handler_for(host, use_varint_format=use_varint_format)
222 )
223 host.set_stream_handler(
224 ID_IDENTIFY_PUSH,
225 custom_identify_push_handler_for(host, use_varint_format=use_varint_format),
226 )
227
228 # Start listening on all available interfaces
229 listen_addrs = get_available_interfaces(port)
230
231 try:
232 async with host.run(listen_addrs):
233 all_addrs = host.get_addrs()
234 logger.info("Listener host ready!")
235 print("Listener host ready!")
236
237 logger.info("Listener ready, listening on:")
238 print("Listener ready, listening on:")
239 for addr in all_addrs:
240 logger.info(f"{addr}")
241 print(f"{addr}")
242
243 logger.info(f"Peer ID: {host.get_id().pretty()}")
244 print(f"Peer ID: {host.get_id().pretty()}")
245
246 # Use the first address as the default for the dialer command
247 default_addr = all_addrs[0]
248 print("\nRun this from the same folder in another console:")
249 if raw_format_flag:
250 print(
251 f"identify-push-listener-dialer-demo -d {default_addr} --raw-format"
252 )
253 else:
254 print(f"identify-push-listener-dialer-demo -d {default_addr}")
255 print("\nWaiting for incoming identify/push requests... (Ctrl+C to exit)")
256
257 # Keep running until interrupted
258 try:
259 await trio.sleep_forever()
260 except KeyboardInterrupt:
261 print("\n🛑 Shutting down listener...")
262 logger.info("Listener interrupted by user")
263 return
264 except Exception as e:
265 logger.error(f"Listener error: {e}")
266 raise
267
268
269async def run_dialer(
270 port: int, destination: str, use_varint_format: bool = True
271) -> None:
272 """Run a host in dialer mode that connects to a listener."""
273 format_name = "length-prefixed" if use_varint_format else "raw protobuf"
274 print(
275 f"\n==== Starting Identify-Push Dialer on port {port} "
276 f"(using {format_name} format) ====\n"
277 )
278
279 # Create key pair for the dialer
280 key_pair = create_new_key_pair()
281
282 # Create the dialer host
283 host = new_host(key_pair=key_pair)
284
285 # Set up the identify and identify/push handlers with specified format
286 host.set_stream_handler(
287 ID_IDENTIFY, identify_handler_for(host, use_varint_format=use_varint_format)
288 )
289 host.set_stream_handler(
290 ID_IDENTIFY_PUSH,
291 identify_push_handler_for(host, use_varint_format=use_varint_format),
292 )
293
294 # Start listening on available interfaces
295 from libp2p.utils.address_validation import get_available_interfaces
296
297 listen_addrs = get_available_interfaces(port)
298
299 async with host.run(listen_addrs):
300 logger.info("Dialer host ready!")
301 print("Dialer host ready!")
302
303 logger.info(f"Listening on: {host.get_addrs()[0]}")
304 print(f"Listening on: {host.get_addrs()[0]}")
305
306 # Parse the destination multiaddress and connect to the listener
307 maddr = multiaddr.Multiaddr(destination)
308 peer_info = info_from_p2p_addr(maddr)
309 logger.info(f"Connecting to peer: {peer_info.peer_id}")
310 print(f"\nConnecting to peer: {peer_info.peer_id}")
311
312 try:
313 await host.connect(peer_info)
314 logger.info("Successfully connected to listener!")
315 print("✅ Successfully connected to listener!")
316 print(f" Connected to: {peer_info.peer_id}")
317 print(f" Full address: {destination}")
318
319 # Push identify information to the listener
320 logger.info("Pushing identify information to listener...")
321 print("\nPushing identify information to listener...")
322
323 try:
324 # Call push_identify_to_peer which returns a boolean
325 success = await push_identify_to_peer(
326 host, peer_info.peer_id, use_varint_format=use_varint_format
327 )
328
329 if success:
330 logger.info("Identify push completed successfully!")
331 print("✅ Identify push completed successfully!")
332
333 logger.info("Example completed successfully!")
334 print("\nExample completed successfully!")
335 else:
336 logger.warning("Identify push didn't complete successfully.")
337 print("\nWarning: Identify push didn't complete successfully.")
338
339 logger.warning("Example completed with warnings.")
340 print("Example completed with warnings.")
341 except Exception as e:
342 error_msg = str(e)
343 logger.error(f"Error during identify push: {error_msg}")
344 print(f"\nError during identify push: {error_msg}")
345
346 # Check for specific format mismatch errors
347 if (
348 "Error parsing message" in error_msg
349 or "DecodeError" in error_msg
350 or "ParseFromString" in error_msg
351 ):
352 print("\n" + "=" * 60)
353 print("FORMAT MISMATCH DETECTED!")
354 print("=" * 60)
355 if use_varint_format:
356 print(
357 "You are using length-prefixed format (default) but the "
358 "listener is using raw protobuf format."
359 )
360 print(
361 "\nTo fix this, run the dialer with the --raw-format flag:"
362 )
363 print(
364 f"identify-push-listener-dialer-demo --raw-format -d "
365 f"{destination}"
366 )
367 else:
368 print("You are using raw protobuf format but the listener")
369 print("is using length-prefixed format (default).")
370 print(
371 "\nTo fix this, run the dialer without the --raw-format "
372 "flag:"
373 )
374 print(f"identify-push-listener-dialer-demo -d {destination}")
375 print("=" * 60)
376
377 logger.error("Example completed with errors.")
378 print("Example completed with errors.")
379 # Continue execution despite the push error
380
381 except Exception as e:
382 error_msg = str(e)
383 if "unable to connect" in error_msg or "SwarmException" in error_msg:
384 print(f"\n❌ Cannot connect to peer: {peer_info.peer_id}")
385 print(f" Address: {destination}")
386 print(f" Error: {error_msg}")
387 print("\n💡 Make sure the peer is running and the address is correct.")
388 return
389 else:
390 logger.error(f"Error during dialer operation: {error_msg}")
391 print(f"\nError during dialer operation: {error_msg}")
392 raise
393
394
395def main() -> None:
396 """Parse arguments and start the appropriate mode."""
397 description = """
398 This program demonstrates the libp2p identify/push protocol.
399 Without arguments, it runs as a listener on random port.
400 With -d parameter, it runs as a dialer on random port.
401
402 Port 0 (default) means the OS will automatically assign an available port.
403 This prevents port conflicts when running multiple instances.
404
405 Use --raw-format to send raw protobuf messages (old format) instead of
406 length-prefixed protobuf messages (new format, default).
407 """
408
409 parser = argparse.ArgumentParser(description=description)
410 parser.add_argument(
411 "-p",
412 "--port",
413 default=0,
414 type=int,
415 help="source port number (0 = random available port)",
416 )
417 parser.add_argument(
418 "-d",
419 "--destination",
420 type=str,
421 help="destination multiaddr string",
422 )
423 parser.add_argument(
424 "--raw-format",
425 action="store_true",
426 help=(
427 "use raw protobuf format (old format) instead of "
428 "length-prefixed (new format)"
429 ),
430 )
431
432 args = parser.parse_args()
433
434 # Determine format: raw format if --raw-format is specified, otherwise
435 # length-prefixed
436 use_varint_format = not args.raw_format
437
438 try:
439 if args.destination:
440 # Run in dialer mode with random available port if not specified
441 trio.run(run_dialer, args.port, args.destination, use_varint_format)
442 else:
443 # Run in listener mode with random available port if not specified
444 trio.run(run_listener, args.port, use_varint_format, args.raw_format)
445 except KeyboardInterrupt:
446 print("\n👋 Goodbye!")
447 logger.info("Application interrupted by user")
448 except Exception as e:
449 print(f"\n❌ Error: {str(e)}")
450 logger.error("Error: %s", str(e))
451 sys.exit(1)
452
453
454if __name__ == "__main__":
455 main()