From d0e997061ee25fb9f7d1d5f4f5c5d592a1f24b1c Mon Sep 17 00:00:00 2001 From: acul71 Date: Tue, 8 Apr 2025 04:41:56 +0200 Subject: [PATCH] doc: add install and getting started documentation --- docs/getting_started.rst | 107 ++++++++++++++++++ docs/index.rst | 11 +- docs/install.rst | 56 +++++++++ docs/quickstart.rst | 4 - .../example_encryption_insecure.py | 54 +++++++++ .../doc-examples/example_encryption_noise.py | 53 +++++++++ .../doc-examples/example_encryption_secio.py | 49 ++++++++ examples/doc-examples/example_multiplexer.py | 62 ++++++++++ .../doc-examples/example_peer_discovery.py | 82 ++++++++++++++ examples/doc-examples/example_running.py | 62 ++++++++++ examples/doc-examples/example_transport.py | 35 ++++++ 11 files changed, 562 insertions(+), 13 deletions(-) create mode 100644 docs/getting_started.rst create mode 100644 docs/install.rst delete mode 100644 docs/quickstart.rst create mode 100644 examples/doc-examples/example_encryption_insecure.py create mode 100644 examples/doc-examples/example_encryption_noise.py create mode 100644 examples/doc-examples/example_encryption_secio.py create mode 100644 examples/doc-examples/example_multiplexer.py create mode 100644 examples/doc-examples/example_peer_discovery.py create mode 100644 examples/doc-examples/example_running.py create mode 100644 examples/doc-examples/example_transport.py diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 00000000..9b9e00a1 --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,107 @@ +Getting Started +=============== + +Welcome to py-libp2p! This guide will walk you through setting up a fully functional libp2p node in Python 🚀 + +Install +------- + +The first step is to install py-libp2p in your project. Follow the installation steps in the :doc:`install` guide. + +Configuring libp2p +------------------ + +If you're new to libp2p, we recommend configuring your node in stages, as this can make troubleshooting configuration issues much easier. In this guide, we'll do just that. + +Basic Setup +~~~~~~~~~~~ + +Now that we have py-libp2p installed, let's configure the minimum needed to get your node running. The only modules libp2p requires are a **Transport** and **Crypto** module. However, we recommend that a basic setup should also have a **Stream Multiplexer** configured. Let's start by setting up a Transport. + +Transports +^^^^^^^^^^ + +Libp2p uses Transports to establish connections between peers over the network. Transports are the components responsible for performing the actual exchange of data between libp2p nodes. You can configure any number of Transports, but you only need 1 to start with. + +For Python, the most common transport is TCP. Here's how to set up a basic TCP transport: + +.. literalinclude:: ../examples/doc-examples/example_transport.py + :language: python + +Connection Encryption +^^^^^^^^^^^^^^^^^^^^^ + +Encryption is an important part of communicating on the libp2p network. Every connection should be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a recommended component of libp2p. + +py-libp2p provides several security transport options: + +1. **Noise** - A modern, flexible, and secure protocol for encryption and authentication +2. **SECIO** - The legacy security protocol used in older versions of libp2p +3. **Insecure** - A transport that provides no encryption (not recommended for production use) + +For most applications, we recommend using the Noise protocol for encryption: + +.. literalinclude:: ../examples/doc-examples/example_encryption_noise.py + :language: python + +If you need to use SECIO (for compatibility with older libp2p implementations): + +.. literalinclude:: ../examples/doc-examples/example_encryption_secio.py + :language: python + +For development or testing purposes only, you can use the insecure transport: + +.. literalinclude:: ../examples/doc-examples/example_encryption_insecure.py + :language: python + +Multiplexing +^^^^^^^^^^^^ + +While multiplexers are not strictly required, they are highly recommended as they improve the effectiveness and efficiency of connections. Adding a multiplexer to your configuration will allow libp2p to run several of its internal protocols, like Identify, as well as allow your application to easily run any number of protocols over a single connection. + +For Python, we can use mplex as our multiplexer: + +.. literalinclude:: ../examples/doc-examples/example_multiplexer.py + :language: python + +Running Libp2p +^^^^^^^^^^^^^^ + +Now that you have configured a **Transport**, **Crypto** and **Stream Multiplexer** module, you can start your libp2p node: + +.. literalinclude:: ../examples/doc-examples/example_running.py + :language: python + +Custom Setup +~~~~~~~~~~~~ + +**NOTE: The current implementation of py-libp2p doesn't yet support dnsaddr multiaddresses. When connecting to bootstrap peers, use direct IP addresses instead.** + +Once your libp2p node is running, it is time to get it connected to the public network. We can do this via peer discovery. + +Peer Discovery +^^^^^^^^^^^^^^ + +Peer discovery is an important part of creating a well connected libp2p node. A static list of peers will often be used to join the network, but it's useful to couple other discovery mechanisms to ensure you're able to discover other peers that are important to your application. + +For Python, you can use the bootstrap list to connect to known peers: + +.. literalinclude:: ../examples/doc-examples/example_peer_discovery.py + :language: python + +Debugging +--------- + +When running libp2p you may want to see what things are happening behind the scenes. You can enable debug logging by setting the appropriate log level: + +.. code:: python + + import logging + + # Set debug level for libp2p + logging.getLogger('libp2p').setLevel(logging.DEBUG) + +What's Next +----------- + +There are a lot of other concepts within `libp2p` that are not covered in this guide. For additional configuration options and examples, check out the :doc:`examples` guide. If you have any problems getting started, or if anything isn't clear, please let us know by submitting an issue! diff --git a/docs/index.rst b/docs/index.rst index 35d38247..036f2204 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,20 +3,13 @@ py-libp2p The Python implementation of the libp2p networking stack -Installation ------------- - -.. code-block:: bash - - python -m pip install py-libp2p - - .. toctree:: :maxdepth: 1 :caption: General introduction - quickstart + install + getting_started release_notes .. toctree:: diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 00000000..163f24b3 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,56 @@ +Install +================ + +Follow the steps below to install `py-libp2p` on your platform. + +**Linux / macOS / Windows** + +1. Create a Python virtual environment: + + .. code:: sh + + python -m venv venv + +2. Activate the virtual environment: + + - **Linux / macOS** + + .. code:: sh + + source venv/bin/activate + + - **Windows (cmd)** + + .. code:: batch + + venv\Scripts\activate.bat + + - **Windows (PowerShell)** + + .. code:: powershell + + venv\Scripts\Activate.ps1 + +3. Install `py-libp2p`: + + .. code:: sh + + python -m pip install libp2p + +Usage +----- +Configuration +~~~~~~~~~~~~~~ +For all the information on how you can configure `py-libp2p`, TODO. + +Limits +~~~~~~~~~~~~~~ +For help configuring your node to resist malicious network peers, TODO. + +Getting started +~~~~~~~~~~~~~~~~ +If you are starting your journey with `py-libp2p`, read the :doc:`getting_started` guide. + +Tutorials and Examples +~~~~~~~~~~~~~~~~~~~~~~~ +You can find multiple examples in the :doc:`examples` guide that will help you understand how to use `py-libp2p` for various scenarios. diff --git a/docs/quickstart.rst b/docs/quickstart.rst deleted file mode 100644 index 27216ca4..00000000 --- a/docs/quickstart.rst +++ /dev/null @@ -1,4 +0,0 @@ -Quickstart -================ - -TODO - add quickstart instructions diff --git a/examples/doc-examples/example_encryption_insecure.py b/examples/doc-examples/example_encryption_insecure.py new file mode 100644 index 00000000..acd947e7 --- /dev/null +++ b/examples/doc-examples/example_encryption_insecure.py @@ -0,0 +1,54 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.security.insecure.transport import ( + PLAINTEXT_PROTOCOL_ID, + InsecureTransport, +) + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create an insecure transport (not recommended for production) + insecure_transport = InsecureTransport( + # local_key_pair: The key pair used for libp2p identity + local_key_pair=key_pair, + # secure_bytes_provider: Optional function to generate secure random bytes + # (defaults to secrets.token_bytes) + secure_bytes_provider=None, # Use default implementation + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {PLAINTEXT_PROTOCOL_ID: insecure_transport} + + # Create a host with the key pair and insecure transport + host = new_host(key_pair=key_pair, sec_opt=security_options) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print( + "libp2p has started with insecure transport " + "(not recommended for production)" + ) + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_encryption_noise.py b/examples/doc-examples/example_encryption_noise.py new file mode 100644 index 00000000..4918dc6f --- /dev/null +++ b/examples/doc-examples/example_encryption_noise.py @@ -0,0 +1,53 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID +from libp2p.security.noise.transport import Transport as NoiseTransport + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a Noise security transport + noise_transport = NoiseTransport( + # local_key_pair: The key pair used for libp2p identity and authentication + libp2p_keypair=key_pair, + # noise_privkey: The private key used for Noise protocol encryption + noise_privkey=key_pair.private_key, + # early_data: Optional data to send during the handshake + # (None means no early data) + early_data=None, + # with_noise_pipes: Whether to use Noise pipes for additional security features + with_noise_pipes=False, + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {NOISE_PROTOCOL_ID: noise_transport} + + # Create a host with the key pair and Noise security + host = new_host(key_pair=key_pair, sec_opt=security_options) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started with Noise encryption") + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_encryption_secio.py b/examples/doc-examples/example_encryption_secio.py new file mode 100644 index 00000000..6204031b --- /dev/null +++ b/examples/doc-examples/example_encryption_secio.py @@ -0,0 +1,49 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.security.secio.transport import ID as SECIO_PROTOCOL_ID +from libp2p.security.secio.transport import Transport as SecioTransport + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a SECIO security transport + secio_transport = SecioTransport( + # local_key_pair: The key pair used for libp2p identity and authentication + local_key_pair=key_pair, + # secure_bytes_provider: Optional function to generate secure random bytes + # (defaults to secrets.token_bytes) + secure_bytes_provider=None, # Use default implementation + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {SECIO_PROTOCOL_ID: secio_transport} + + # Create a host with the key pair and SECIO security + host = new_host(key_pair=key_pair, sec_opt=security_options) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started with SECIO encryption") + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_multiplexer.py b/examples/doc-examples/example_multiplexer.py new file mode 100644 index 00000000..7cbf29f0 --- /dev/null +++ b/examples/doc-examples/example_multiplexer.py @@ -0,0 +1,62 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID +from libp2p.security.noise.transport import Transport as NoiseTransport +from libp2p.stream_muxer.mplex.mplex import ( + MPLEX_PROTOCOL_ID, +) + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a Noise security transport + noise_transport = NoiseTransport( + # local_key_pair: The key pair used for libp2p identity and authentication + libp2p_keypair=key_pair, + # noise_privkey: The private key used for Noise protocol encryption + noise_privkey=key_pair.private_key, + # early_data: Optional data to send during the handshake + # (None means no early data) + early_data=None, + # with_noise_pipes: Whether to use Noise pipes for additional security features + with_noise_pipes=False, + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {NOISE_PROTOCOL_ID: noise_transport} + + # Create a muxer options dictionary mapping protocol ID to muxer class + # We don't need to instantiate the muxer here, the host will do that for us + muxer_options = {MPLEX_PROTOCOL_ID: None} + + # Create a host with the key pair, Noise security, and mplex multiplexer + host = new_host( + key_pair=key_pair, sec_opt=security_options, muxer_opt=muxer_options + ) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started with Noise encryption and mplex multiplexing") + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_peer_discovery.py b/examples/doc-examples/example_peer_discovery.py new file mode 100644 index 00000000..dd789ad0 --- /dev/null +++ b/examples/doc-examples/example_peer_discovery.py @@ -0,0 +1,82 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.peer.peerinfo import ( + info_from_p2p_addr, +) +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID +from libp2p.security.noise.transport import Transport as NoiseTransport +from libp2p.stream_muxer.mplex.mplex import ( + MPLEX_PROTOCOL_ID, +) + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a Noise security transport + noise_transport = NoiseTransport( + # local_key_pair: The key pair used for libp2p identity and authentication + libp2p_keypair=key_pair, + # noise_privkey: The private key used for Noise protocol encryption + noise_privkey=key_pair.private_key, + # early_data: Optional data to send during the handshake + # (None means no early data) + early_data=None, + # with_noise_pipes: Whether to use Noise pipes for additional security features + with_noise_pipes=False, + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {NOISE_PROTOCOL_ID: noise_transport} + + # Create a muxer options dictionary mapping protocol ID to muxer class + # We don't need to instantiate the muxer here, the host will do that for us + muxer_options = {MPLEX_PROTOCOL_ID: None} + + # Create a host with the key pair, Noise security, and mplex multiplexer + host = new_host( + key_pair=key_pair, sec_opt=security_options, muxer_opt=muxer_options + ) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started") + print("libp2p is listening on:", host.get_addrs()) + + # Connect to bootstrap peers manually + bootstrap_list = [ + "/dnsaddr/bootstrap.libp2p.io/p2p/" + + "QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/dnsaddr/bootstrap.libp2p.io/p2p/" + + "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + ] + + for addr in bootstrap_list: + try: + peer_info = info_from_p2p_addr(multiaddr.Multiaddr(addr)) + await host.connect(peer_info) + print(f"Connected to {peer_info.peer_id.to_string()}") + except Exception as e: + print(f"Failed to connect to {addr}: {e}") + + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_running.py b/examples/doc-examples/example_running.py new file mode 100644 index 00000000..c9d3d053 --- /dev/null +++ b/examples/doc-examples/example_running.py @@ -0,0 +1,62 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) +from libp2p.security.noise.transport import PROTOCOL_ID as NOISE_PROTOCOL_ID +from libp2p.security.noise.transport import Transport as NoiseTransport +from libp2p.stream_muxer.mplex.mplex import ( + MPLEX_PROTOCOL_ID, +) + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a Noise security transport + noise_transport = NoiseTransport( + # local_key_pair: The key pair used for libp2p identity and authentication + libp2p_keypair=key_pair, + # noise_privkey: The private key used for Noise protocol encryption + noise_privkey=key_pair.private_key, + # early_data: Optional data to send during the handshake + # (None means no early data) + early_data=None, + # with_noise_pipes: Whether to use Noise pipes for additional security features + with_noise_pipes=False, + ) + + # Create a security options dictionary mapping protocol ID to transport + security_options = {NOISE_PROTOCOL_ID: noise_transport} + + # Create a muxer options dictionary mapping protocol ID to muxer class + # We don't need to instantiate the muxer here, the host will do that for us + muxer_options = {MPLEX_PROTOCOL_ID: None} + + # Create a host with the key pair, Noise security, and mplex multiplexer + host = new_host( + key_pair=key_pair, sec_opt=security_options, muxer_opt=muxer_options + ) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started") + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main) diff --git a/examples/doc-examples/example_transport.py b/examples/doc-examples/example_transport.py new file mode 100644 index 00000000..e981fa7d --- /dev/null +++ b/examples/doc-examples/example_transport.py @@ -0,0 +1,35 @@ +import secrets + +import multiaddr +import trio + +from libp2p import ( + new_host, +) +from libp2p.crypto.secp256k1 import ( + create_new_key_pair, +) + + +async def main(): + # Create a key pair for the host + secret = secrets.token_bytes(32) + key_pair = create_new_key_pair(secret) + + # Create a host with the key pair + host = new_host(key_pair=key_pair) + + # Configure the listening address + port = 8000 + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + # Start the host + async with host.run(listen_addrs=[listen_addr]): + print("libp2p has started with TCP transport") + print("libp2p is listening on:", host.get_addrs()) + # Keep the host running + await trio.sleep_forever() + + +# Run the async function +trio.run(main)