Skip to main content

Python implementation of jbenet's multiaddr

Project description

py-multiaddr

https://img.shields.io/pypi/v/multiaddr.svg https://github.com/multiformats/py-multiaddr/actions/workflows/ci.yml/badge.svg https://codecov.io/github/multiformats/py-multiaddr/coverage.svg?branch=master Documentation Status

multiaddr implementation in Python

Installation

pip install multiaddr

Requirements

  • Python 3.10+

  • trio (for async DNS resolution)

Usage

Simple

from multiaddr import Multiaddr

# construct from a string
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

# construct from bytes
#m2 = Multiaddr(bytes_addr=m1.to_bytes()) # deprecated
m2 = Multiaddr(m1.to_bytes())

assert str(m1) == "/ip4/127.0.0.1/udp/1234"
assert str(m1) == str(m2)
assert m1.to_bytes() == m2.to_bytes()
assert m1 == m2
assert m2 == m1
assert not (m1 != m2)
assert not (m2 != m1)

Protocols

from multiaddr import Multiaddr

m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

# get the multiaddr protocol description objects
m1.protocols()
# [Protocol(code=4, name='ip4', size=32), Protocol(code=17, name='udp', size=16)]

En/decapsulate

from multiaddr import Multiaddr

m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
m1.encapsulate(Multiaddr("/sctp/5678"))
# <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
m1.decapsulate(Multiaddr("/udp"))
# <Multiaddr /ip4/127.0.0.1>

# Decapsulate by protocol code
m2 = Multiaddr("/ip4/192.168.1.1/tcp/8080/udp/1234")
m2.decapsulate_code(6)  # TCP protocol code
# <Multiaddr /ip4/192.168.1.1>

# Decapsulate multiple layers
m3 = Multiaddr("/ip4/10.0.0.1/tcp/443/tls/p2p/QmPeer")
m3.decapsulate_code(6)  # Remove TCP and everything after
# <Multiaddr /ip4/10.0.0.1>

Tunneling

Multiaddr allows expressing tunnels very nicely.

printer = Multiaddr("/ip4/192.168.0.13/tcp/80")
proxy = Multiaddr("/ip4/10.20.30.40/tcp/443")
printerOverProxy = proxy.encapsulate(printer)
print(printerOverProxy)
# /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80

proxyAgain = printerOverProxy.decapsulate(printer)
print(proxyAgain)
# /ip4/10.20.30.40/tcp/443

DNS Resolution

Multiaddr supports DNS-based address resolution using the DNSADDR protocol. This is particularly useful for resolving bootstrap node addresses and maintaining peer IDs during resolution.

from multiaddr import Multiaddr
import trio

# Basic DNS resolution
ma = Multiaddr("/dns/example.com")
resolved = await ma.resolve()
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

# DNSADDR with peer ID (bootstrap node style)
ma_with_peer = Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
resolved_with_peer = await ma_with_peer.resolve()
print(resolved_with_peer)
# [Multiaddr("/ip4/147.75.83.83/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")]

# DNS4 and DNS6 resolution (IPv4/IPv6 specific)
ma_dns4 = Multiaddr("/dns4/example.com/tcp/443")
resolved_dns4 = await ma_dns4.resolve()
print(resolved_dns4)
# [Multiaddr("/ip4/93.184.216.34/tcp/443")]

ma_dns6 = Multiaddr("/dns6/example.com/tcp/443")
resolved_dns6 = await ma_dns6.resolve()
print(resolved_dns6)
# [Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946/tcp/443")]

# Using the DNS resolver directly
from multiaddr.resolvers import DNSResolver
resolver = DNSResolver()
resolved = await resolver.resolve(ma)
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

# Peer ID preservation test
original_peer_id = ma_with_peer.get_peer_id()
print(f"Original peer ID: {original_peer_id}")
# Original peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

for resolved_addr in resolved_with_peer:
    preserved_peer_id = resolved_addr.get_peer_id()
    print(f"Resolved peer ID: {preserved_peer_id}")
    # Resolved peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

For comprehensive examples including bootstrap node resolution, protocol comparison, and py-libp2p integration, see the DNS examples in the examples directory.

Thin Waist Address Validation

Multiaddr provides thin waist address validation functionality to process multiaddrs and expand wildcard addresses to all available network interfaces. This is particularly useful for server configuration, network discovery, and dynamic port management.

from multiaddr import Multiaddr
from multiaddr.utils import get_thin_waist_addresses, get_network_addrs

# Network interface discovery
ipv4_addrs = get_network_addrs(4)
print(f"Available IPv4 addresses: {ipv4_addrs}")
# Available IPv4 addresses: ['192.168.1.12', '10.152.168.99']

# Specific address (no expansion)
addr = Multiaddr("/ip4/192.168.1.100/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.100/tcp/8080>]

# IPv4 wildcard expansion
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/8080>, <Multiaddr /ip4/10.152.168.99/tcp/8080>]

# IPv6 wildcard expansion
addr = Multiaddr("/ip6/::/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip6/::1/tcp/8080>, <Multiaddr /ip6/fd9b:9eba:8224:1:41a1:8939:231a:b414/tcp/8080>]

# Port override
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr, port=9000)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/9000>, <Multiaddr /ip4/10.152.168.99/tcp/9000>]

# UDP transport support
addr = Multiaddr("/ip4/0.0.0.0/udp/1234")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/udp/1234>, <Multiaddr /ip4/10.152.168.99/udp/1234>]

# Server binding scenario
wildcard = Multiaddr("/ip4/0.0.0.0/tcp/8080")
interfaces = get_thin_waist_addresses(wildcard)
print("Available interfaces for server binding:")
for i, interface in enumerate(interfaces, 1):
    print(f"  {i}. {interface}")
# Available interfaces for server binding:
#   1. /ip4/192.168.1.12/tcp/8080
#   2. /ip4/10.152.168.99/tcp/8080

For comprehensive examples including error handling, practical usage scenarios, and detailed network interface information, see the thin waist examples in the examples directory.

Features

  • Multiaddr Protocol Support: Full support for the multiaddr specification

  • DNS Resolution: Async DNS and DNSADDR resolution with trio

  • Thin Waist Validation: Network interface discovery and wildcard expansion

  • Protocol Support: IPv4, IPv6, TCP, UDP, DNS, DNS4, DNS6, DNSADDR, p2p, p2p-circuit, onion, onion3, quic, tls, and more

  • Type Safety: Full type hints and mypy support

  • Modern Python: Python 3.10+ support with modern tooling

Maintainers

Original author: @sbuss.

Current maintainers: @acul71, @pacrob, @manusheel.

Contribute

Contributions welcome. Please check out the issues.

Check out our contributing document for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS Code of Conduct.

Development

For development setup, see py-multiaddr contributing.

# Clone the repository
git clone https://github.com/multiformats/py-multiaddr.git
cd py-multiaddr

# Install in development mode
pip install -e ".[dev]"

# Run the development workflow
make pr

License

Dual-licensed:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multiaddr-0.2.0.tar.gz (58.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multiaddr-0.2.0-py3-none-any.whl (40.4 kB view details)

Uploaded Python 3

File details

Details for the file multiaddr-0.2.0.tar.gz.

File metadata

  • Download URL: multiaddr-0.2.0.tar.gz
  • Upload date:
  • Size: 58.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for multiaddr-0.2.0.tar.gz
Algorithm Hash digest
SHA256 acb6b25c332ec1b2f1f8fef8d03a8c63385d34a87d690df0f4bba43cdf6efe8d
MD5 6a8b64243645a1567dd3a291a48c2a97
BLAKE2b-256 c7104e26a8577cfce1c0febc8d83087e1373e93c695c6e73ad010546fb67e229

See more details on using hashes here.

File details

Details for the file multiaddr-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: multiaddr-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 40.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for multiaddr-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bcff7bf3d7de3d6da0b865b25423bcb411de1d20d70cc6abfacf75170d17866c
MD5 9cbe507343131c682f18b48bd2fef7d3
BLAKE2b-256 b51356e503d01218d1ca27ea9fda862045a4b400cae5e756f47315f5aaba0eee

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page