From ccd157eb61a707e4dd39da33f25c2b884d8d2cb3 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Tue, 7 Oct 2025 14:31:45 +0530 Subject: [PATCH 01/17] Add sni configs in the registry --- multiaddr/protocols.py | 1 + tests/test_multiaddr.py | 1 + 2 files changed, 2 insertions(+) diff --git a/multiaddr/protocols.py b/multiaddr/protocols.py index 028b3ab..e255ae9 100644 --- a/multiaddr/protocols.py +++ b/multiaddr/protocols.py @@ -152,6 +152,7 @@ def __repr__(self) -> str: Protocol(P_DNS4, "dns4", "domain"), Protocol(P_DNS6, "dns6", "domain"), Protocol(P_DNSADDR, "dnsaddr", "domain"), + Protocol(P_SNI, "sni", "domain"), Protocol(P_SCTP, "sctp", "uint16be"), Protocol(P_UDT, "udt", None), Protocol(P_UTP, "utp", None), diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index 3a62556..4fab84e 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -129,6 +129,7 @@ def test_invalid(addr_str): "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio", "/dns/example.com", "/dns4/موقع.وزارة-الاتصالات.مصر", + "/ip4/127.0.0.1/tcp/443/tls/sni/example.com/http/http-path/foo", ], ) # nopep8 def test_valid(addr_str): From 3463454e3ed09e2febe6bbe4017493fe3c6e68a6 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Tue, 7 Oct 2025 14:37:04 +0530 Subject: [PATCH 02/17] Add newsfragment --- newsfragments/97.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/97.feature.rst diff --git a/newsfragments/97.feature.rst b/newsfragments/97.feature.rst new file mode 100644 index 0000000..16ef24b --- /dev/null +++ b/newsfragments/97.feature.rst @@ -0,0 +1 @@ +Add the sni protocol support in py-multiaddr in reference with go-multiaddr From 740d495d854ad1e95f5bb3ce1ebf0c53aa6e825f Mon Sep 17 00:00:00 2001 From: lla-dane Date: Tue, 7 Oct 2025 14:59:26 +0530 Subject: [PATCH 03/17] Add noise configs in the registry --- multiaddr/protocols.py | 1 + tests/test_multiaddr.py | 1 + 2 files changed, 2 insertions(+) diff --git a/multiaddr/protocols.py b/multiaddr/protocols.py index e255ae9..bef3838 100644 --- a/multiaddr/protocols.py +++ b/multiaddr/protocols.py @@ -153,6 +153,7 @@ def __repr__(self) -> str: Protocol(P_DNS6, "dns6", "domain"), Protocol(P_DNSADDR, "dnsaddr", "domain"), Protocol(P_SNI, "sni", "domain"), + Protocol(P_NOISE, "noise", None), Protocol(P_SCTP, "sctp", "uint16be"), Protocol(P_UDT, "udt", None), Protocol(P_UTP, "utp", None), diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index 4fab84e..b6562e7 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -125,6 +125,7 @@ def test_invalid(addr_str): "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/unix/a/b/c/d/e", "/unix/stdio", + "/ip4/127.0.0.1/tcp/127/noise", "/ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f", "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio", "/dns/example.com", From 73fd6f81e52d40edc4d9eea885e8f1589e193a2e Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 10 Oct 2025 15:43:57 +0530 Subject: [PATCH 04/17] Implemented certhash protcol - Added the protocol utils in certhash.py - Added the internal logic test suite in test_protocols.py - Added the certhash multiaddr test cases in test_multiaddr.py --- multiaddr/codecs/certhash.py | 84 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 + tests/test_multiaddr.py | 2 + tests/test_protocols.py | 66 +++++++++++++++++++++++++++- 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 multiaddr/codecs/certhash.py diff --git a/multiaddr/codecs/certhash.py b/multiaddr/codecs/certhash.py new file mode 100644 index 0000000..b613e51 --- /dev/null +++ b/multiaddr/codecs/certhash.py @@ -0,0 +1,84 @@ +from typing import Any +import multibase +import multihash + +from ..codecs import CodecBase +from ..exceptions import BinaryParseError + +SIZE = -1 +IS_PATH = False + +class Codec(CodecBase): + """ + Codec for certificate hashes (certhash). + + A certhash is a multihash of a certificate, encoded as a multibase string + using the 'base64url' encoding. + """ + + SIZE = SIZE + IS_PATH = IS_PATH + + def validate(self, b: bytes) -> None: + """ + Validates that the byte representation is a valid multihash. + + Args: + b: The bytes to validate. + + Raises: + ValueError: If the bytes cannot be decoded as a multihash. + """ + try: + multihash.decode(b) + except Exception as e: + raise ValueError(f"Invalid certhash: not a valid multihash") from e + + def to_bytes(self, proto: Any, string: str) -> bytes: + """ + Converts the multibase string representation of a certhash to bytes. + + This involves decoding the multibase string and then validating that + the resulting bytes are a valid multihash. + + Args: + proto: The multiaddr protocol code (unused). + string: The string representation of the certhash. + + Returns: + The raw multihash bytes. + + Raises: + ValueError: If the string is not valid multibase or not a multihash. + """ + try: + # Decode the multibase string to get the raw multihash bytes. + decoded_bytes = multibase.decode(string) + except Exception as e: + raise ValueError(f"Failed to decode multibase string: {string}") from e + + # Validate that the decoded bytes are a valid multihash. + self.validate(decoded_bytes) + return decoded_bytes + + def to_string(self, proto: Any, buf: bytes) -> str: + """ + Converts the raw multihash bytes of a certhash to its string form. + + This involves validating the bytes first and then encoding them as a + 'base64url' multibase string. + + Args: + proto: The multiaddr protocol code (unused). + buf: The raw multihash bytes. + + Returns: + The multibase string representation of the certhash. + """ + # Validate the bytes before encoding. + self.validate(buf) + + # Encode the bytes using base64url, which is standard for certhash. + # The result from `multibase.encode` is bytes, so we decode to a string. + encoded_string = multibase.encode('base64url', buf) + return encoded_string.decode('utf-8') \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 6ac8a75..9a837cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,8 @@ dependencies = [ "psutil", "py-cid >= 0.3.1", "py-multicodec >= 0.2.0", + "py-multibase", + "py-multihash", "trio-typing>=0.0.4", "trio>=0.26.0", "varint", diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index b6562e7..b52e803 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -67,6 +67,8 @@ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu:80", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq:-1", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu@", + "/ip4/127.0.0.1/udp/1234/quic-v1/webtransport/certhash/b2uaraocy6yrdblb4sfptaddgimjmmpy", + "/ip4/127.0.0.1/udp/1234/quic-v1/webtransport/certhash/b2uaraocy6yrdblb4sfptaddgimjmmpy/certhash/zQmbWTwYGcmdyK9CYfNBcfs9nhZs17a6FQ4Y8oea278xx41", "/udp/1234/sctp", "/udp/1234/udt/1234", "/udp/1234/utp/1234", diff --git a/tests/test_protocols.py b/tests/test_protocols.py index bcac66c..86fa009 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -1,11 +1,13 @@ import base64 import os +import multibase +import multihash import pytest import varint from multiaddr import Multiaddr, exceptions, protocols -from multiaddr.codecs import garlic32, garlic64, http_path, ipcidr, memory +from multiaddr.codecs import garlic32, garlic64, certhash, http_path, ipcidr, memory from multiaddr.exceptions import BinaryParseError, StringParseError @@ -559,3 +561,65 @@ def test_ipcidr_invalid_bytes_inputs(): with pytest.raises(ValueError): codec.validate(b"\x01\x02") + + +# --------CERT-HASH--------- + +VALID_MULTIHASH_BYTES = multihash.encode(b"hello world", "sha2-256") +VALID_CERTHASH_STRING = multibase.encode("base64url", VALID_MULTIHASH_BYTES).decode("utf-8") + +INVALID_BYTES = b"this is not a multihash" +INVALID_CONTENT_STRING = multibase.encode("base64url", INVALID_BYTES).decode("utf-8") + + +def test_certhash_valid_roundtrip(): + codec = certhash.Codec() + b = codec.to_bytes(None, VALID_CERTHASH_STRING) + assert isinstance(b, bytes) + assert b == VALID_MULTIHASH_BYTES + + +def test_certhash_invalid_multihash_bytes_raises(): + """ + Tests that calling to_string() with bytes that are not a valid + multihash raises a ValueError. + """ + codec = certhash.Codec() + with pytest.raises(ValueError): + codec.to_string(None, INVALID_BYTES) + + +def test_certhash_valid_multibase_but_invalid_content_raises(): + """ + Tests that to_bytes() raises an error if the string is valid multibase + but its decoded content is not a valid multihash. + """ + codec = certhash.Codec() + with pytest.raises(ValueError): + codec.to_bytes(None, INVALID_CONTENT_STRING) + + +def test_certhash_invalid_multibase_string_raises(): + """ + Tests that passing a string with an invalid multibase prefix or + encoding raises an error. + """ + codec = certhash.Codec() + # 'z' is a valid multibase prefix, but the content is not valid base58. + invalid_string = "z-this-is-not-valid" + with pytest.raises(Exception): # Catches errors from the multibase library + codec.to_bytes(None, invalid_string) + + +def test_certhash_memory_validate_function(): + """ + Directly tests the validate method. + """ + codec = certhash.Codec() + + # A valid multihash should not raise an error + codec.validate(VALID_MULTIHASH_BYTES) + + # Invalid bytes should raise a ValueError + with pytest.raises(ValueError): + codec.validate(INVALID_BYTES) From d3df626fe275eebed5aa9002a2d62d73b68d6630 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 10 Oct 2025 15:58:45 +0530 Subject: [PATCH 05/17] Fix lint errors --- multiaddr/codecs/certhash.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/multiaddr/codecs/certhash.py b/multiaddr/codecs/certhash.py index b613e51..e6b673c 100644 --- a/multiaddr/codecs/certhash.py +++ b/multiaddr/codecs/certhash.py @@ -1,24 +1,25 @@ from typing import Any -import multibase + +import multibase import multihash from ..codecs import CodecBase -from ..exceptions import BinaryParseError SIZE = -1 IS_PATH = False + class Codec(CodecBase): """ Codec for certificate hashes (certhash). - + A certhash is a multihash of a certificate, encoded as a multibase string using the 'base64url' encoding. """ - + SIZE = SIZE IS_PATH = IS_PATH - + def validate(self, b: bytes) -> None: """ Validates that the byte representation is a valid multihash. @@ -32,7 +33,7 @@ def validate(self, b: bytes) -> None: try: multihash.decode(b) except Exception as e: - raise ValueError(f"Invalid certhash: not a valid multihash") from e + raise ValueError("Invalid certhash: not a valid multihash") from e def to_bytes(self, proto: Any, string: str) -> bytes: """ @@ -60,7 +61,7 @@ def to_bytes(self, proto: Any, string: str) -> bytes: # Validate that the decoded bytes are a valid multihash. self.validate(decoded_bytes) return decoded_bytes - + def to_string(self, proto: Any, buf: bytes) -> str: """ Converts the raw multihash bytes of a certhash to its string form. @@ -80,5 +81,5 @@ def to_string(self, proto: Any, buf: bytes) -> str: # Encode the bytes using base64url, which is standard for certhash. # The result from `multibase.encode` is bytes, so we decode to a string. - encoded_string = multibase.encode('base64url', buf) - return encoded_string.decode('utf-8') \ No newline at end of file + encoded_string = multibase.encode("base64url", buf) + return encoded_string.decode("utf-8") From 185542a2215e73946ab997f5d576417e9c832f74 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Sat, 11 Oct 2025 13:54:01 +0530 Subject: [PATCH 06/17] Added webrtc-direct and webrtc - Added the protocols in the registry in protcols.py - Added the maddr test cases in test_multiaddr.py --- multiaddr/protocols.py | 4 ++++ tests/test_multiaddr.py | 7 +++++++ tests/test_thin_waist_addresses.py | 7 ------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/multiaddr/protocols.py b/multiaddr/protocols.py index bef3838..de25765 100644 --- a/multiaddr/protocols.py +++ b/multiaddr/protocols.py @@ -81,6 +81,8 @@ P_SNI = 0x01C1 P_NOISE = 0x01C6 P_WEBTRANSPORT = 0x01D1 +P_WEBRTC_DIRECT = 0x118 +P_WEBRTC = 0X119 P_MEMORY = 0x309 @@ -176,6 +178,8 @@ def __repr__(self) -> str: Protocol(P_P2P_CIRCUIT, "p2p-circuit", None), Protocol(P_WEBTRANSPORT, "webtransport", None), Protocol(P_UNIX, "unix", "fspath"), + Protocol(P_WEBRTC_DIRECT, "webrtc-direct", None), + Protocol(P_WEBRTC, "webrtc", None), Protocol(P_MEMORY, "memory", "memory"), ] diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index b52e803..8911bfc 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -133,6 +133,13 @@ def test_invalid(addr_str): "/dns/example.com", "/dns4/موقع.وزارة-الاتصالات.مصر", "/ip4/127.0.0.1/tcp/443/tls/sni/example.com/http/http-path/foo", + "/memory/4", + "/http-path/tmp%2Fbar", + "/http-path/tmp%2Fbar%2Fbaz", + "/http-path/foo", + "/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct", + "/ip4/127.0.0.1/tcp/127/webrtc-direct", + "/ip4/127.0.0.1/tcp/127/webrtc", ], ) # nopep8 def test_valid(addr_str): diff --git a/tests/test_thin_waist_addresses.py b/tests/test_thin_waist_addresses.py index 7f8dca3..4ab47ce 100644 --- a/tests/test_thin_waist_addresses.py +++ b/tests/test_thin_waist_addresses.py @@ -21,13 +21,6 @@ def test_specific_address_override_port(): addrs = get_thin_waist_addresses(input_addr, 100) assert addrs == [Multiaddr("/ip4/123.123.123.123/tcp/100")] - -def test_ignore_non_thin_waist(): - # Should raise StringParseError for unknown protocol (e.g. /webrtc) - with pytest.raises(StringParseError): - Multiaddr("/ip4/123.123.123.123/udp/1234/webrtc") - - def test_ipv4_wildcard(): input_addr = Multiaddr("/ip4/0.0.0.0/tcp/1234") addrs = get_thin_waist_addresses(input_addr) From 07b5384744f63f991ccadb160d76eacb29f52128 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Sat, 11 Oct 2025 14:07:15 +0530 Subject: [PATCH 07/17] Fix lint errors --- multiaddr/protocols.py | 4 +++- newsfragments/97.feature.rst | 8 +++++++- tests/test_multiaddr.py | 2 +- tests/test_thin_waist_addresses.py | 4 +--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/multiaddr/protocols.py b/multiaddr/protocols.py index de25765..43c9ffa 100644 --- a/multiaddr/protocols.py +++ b/multiaddr/protocols.py @@ -82,8 +82,9 @@ P_NOISE = 0x01C6 P_WEBTRANSPORT = 0x01D1 P_WEBRTC_DIRECT = 0x118 -P_WEBRTC = 0X119 +P_WEBRTC = 0x119 P_MEMORY = 0x309 +P_CERTHASH = 0x1D2 class Protocol: @@ -181,6 +182,7 @@ def __repr__(self) -> str: Protocol(P_WEBRTC_DIRECT, "webrtc-direct", None), Protocol(P_WEBRTC, "webrtc", None), Protocol(P_MEMORY, "memory", "memory"), + Protocol(P_CERTHASH, "certhash", "certhash"), ] diff --git a/newsfragments/97.feature.rst b/newsfragments/97.feature.rst index 16ef24b..2d728a5 100644 --- a/newsfragments/97.feature.rst +++ b/newsfragments/97.feature.rst @@ -1 +1,7 @@ -Add the sni protocol support in py-multiaddr in reference with go-multiaddr +Added the following protocols in reference with go-multiaddr + +- SNI: 0x01C1 +- NOISE: 0x01C6 +- CERTHASH: +- WEBRTC: +- WEBRTC-DIRECT: diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index 8911bfc..35cb74a 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -133,7 +133,7 @@ def test_invalid(addr_str): "/dns/example.com", "/dns4/موقع.وزارة-الاتصالات.مصر", "/ip4/127.0.0.1/tcp/443/tls/sni/example.com/http/http-path/foo", - "/memory/4", + "/memory/4", "/http-path/tmp%2Fbar", "/http-path/tmp%2Fbar%2Fbaz", "/http-path/foo", diff --git a/tests/test_thin_waist_addresses.py b/tests/test_thin_waist_addresses.py index 4ab47ce..7bd2c33 100644 --- a/tests/test_thin_waist_addresses.py +++ b/tests/test_thin_waist_addresses.py @@ -1,7 +1,4 @@ -import pytest - from multiaddr import Multiaddr -from multiaddr.exceptions import StringParseError from multiaddr.utils import get_thin_waist_addresses @@ -21,6 +18,7 @@ def test_specific_address_override_port(): addrs = get_thin_waist_addresses(input_addr, 100) assert addrs == [Multiaddr("/ip4/123.123.123.123/tcp/100")] + def test_ipv4_wildcard(): input_addr = Multiaddr("/ip4/0.0.0.0/tcp/1234") addrs = get_thin_waist_addresses(input_addr) From 3a61863bbe573d593243f57bd44a6db5aabf4ffd Mon Sep 17 00:00:00 2001 From: acul71 Date: Sun, 12 Oct 2025 22:48:48 -0400 Subject: [PATCH 08/17] Fix multihash API usage and correct newsfragment issue number - Fix multihash.encode() to multihash.digest().encode() in tests - Rename newsfragment from 97.feature.rst to 181.feature.rst to match issue #181 - All tests now pass with correct multihash API usage --- newsfragments/{97.feature.rst => 181.feature.rst} | 0 tests/test_protocols.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename newsfragments/{97.feature.rst => 181.feature.rst} (100%) diff --git a/newsfragments/97.feature.rst b/newsfragments/181.feature.rst similarity index 100% rename from newsfragments/97.feature.rst rename to newsfragments/181.feature.rst diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 86fa009..7aceb2e 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -565,7 +565,7 @@ def test_ipcidr_invalid_bytes_inputs(): # --------CERT-HASH--------- -VALID_MULTIHASH_BYTES = multihash.encode(b"hello world", "sha2-256") +VALID_MULTIHASH_BYTES = multihash.digest(b"hello world", "sha2-256").encode() VALID_CERTHASH_STRING = multibase.encode("base64url", VALID_MULTIHASH_BYTES).decode("utf-8") INVALID_BYTES = b"this is not a multihash" From 263c562f1c13042817eec0c1067e839127f07780 Mon Sep 17 00:00:00 2001 From: acul71 Date: Sun, 12 Oct 2025 22:58:42 -0400 Subject: [PATCH 09/17] Fix multihash API usage for CI compatibility - Use multihash.encode() instead of multihash.digest().encode() - This matches the py-multihash library API used in CI - All tests now pass with correct multihash library version --- tests/test_protocols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 7aceb2e..86fa009 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -565,7 +565,7 @@ def test_ipcidr_invalid_bytes_inputs(): # --------CERT-HASH--------- -VALID_MULTIHASH_BYTES = multihash.digest(b"hello world", "sha2-256").encode() +VALID_MULTIHASH_BYTES = multihash.encode(b"hello world", "sha2-256") VALID_CERTHASH_STRING = multibase.encode("base64url", VALID_MULTIHASH_BYTES).decode("utf-8") INVALID_BYTES = b"this is not a multihash" From 218dad8261689da8d1c2d34ac8eebaebb1490e4a Mon Sep 17 00:00:00 2001 From: lla-dane Date: Wed, 22 Oct 2025 21:14:33 +0530 Subject: [PATCH 10/17] Fix merge conflicts --- tests/test_protocols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 86fa009..53a94b5 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -7,7 +7,7 @@ import varint from multiaddr import Multiaddr, exceptions, protocols -from multiaddr.codecs import garlic32, garlic64, certhash, http_path, ipcidr, memory +from multiaddr.codecs import certhash, garlic32, garlic64, http_path, ipcidr, memory from multiaddr.exceptions import BinaryParseError, StringParseError From d10a92ac912638a3596678927831179c7ae57347 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 10:37:55 +0530 Subject: [PATCH 11/17] Corrected the test function name --- tests/test_protocols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 53a94b5..65cddd1 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -611,7 +611,7 @@ def test_certhash_invalid_multibase_string_raises(): codec.to_bytes(None, invalid_string) -def test_certhash_memory_validate_function(): +def test_certhash_validate_function(): """ Directly tests the validate method. """ From bb66dd165d74d7c756967f08378ca6b2e257310c Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 13:51:58 +0530 Subject: [PATCH 12/17] Added examples for SNI: examples/sni/sni_examples.py --- examples/sni/sni_examples.py | 259 +++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 examples/sni/sni_examples.py diff --git a/examples/sni/sni_examples.py b/examples/sni/sni_examples.py new file mode 100644 index 0000000..d75d06a --- /dev/null +++ b/examples/sni/sni_examples.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +""" +SNI (Server Name Indication) protocol examples for py-multiaddr. + +This script demonstrates how to use the 'sni' protocol in py-multiaddr, +which is typically used to specify the hostname during the TLS handshake. + +## Overview + +This script shows various examples of SNI protocol usage: + +1. **Basic SNI Usage**: Creating and parsing a basic /sni/hostname address. +2. **Protocol Validation**: Testing valid and invalid hostnames. +3. **Binary Encoding/Decoding**: Working with binary representations. +4. **Multiaddr Integration**: Using 'sni' in a realistic multiaddr string + (e.g., /dns/google.com/tcp/443/tls/sni/google.com). +5. **Error Handling**: Demonstrating errors for invalid domain names. + +# Expected Output + +When you run this script, you should see output similar to: + +``` +SNI Protocol Examples +================================================== +=== Basic SNI Usage === +Original SNI: /sni/protocol.ai +Protocols: ['sni'] +Extracted Value: protocol.ai +Binary length: 14 bytes +Valid SNI address: True + +=== Protocol Validation === +Testing valid SNI: /sni/protocol.ai + Valid: True + Value: protocol.ai + +Testing invalid SNI (invalid domain chars): /sni/invalid_hostname! + Valid: False Error: invalid_hostname! is not a valid domain name + +Testing invalid SNI (empty value): /sni/ + Valid: False Error: is not a valid domain name + +=== Binary Encoding/Decoding === +SNI binary operations: + Original: /sni/protocol.ai + Binary: 14 bytes + Round-trip: /sni/protocol.ai + Match: True + +=== Multiaddr Integration === +Complex multiaddr with SNI: + Address: /dns/google.com/tcp/443/tls/sni/google.com + Protocols: ['dns', 'tcp', 'tls', 'sni'] + Extracted SNI value: google.com + Match: True + +================================================== +All examples completed! +``` + +## Key features Demonstrated + +- **SNI Protocol**: Represents a Server Name Indication value. +- **Validation**: Ensures the value is a valid domain name (as it uses the 'domain' transcoder). +- **Binary Operation**: Encoding and decoding to/from binary format. +- **Multiaddr Integration**: Using 'sni' within a '/tls' protocol. +- **Error Handling**: Proper error handling for invalid domain names. + +## Requirements + +- Python 3.10+ +- py-multiaddr library + +## Usage + +```bash +python examples/sni_examples.py +``` +""" + +from multiaddr import Multiaddr + +# A valid domain name to use for SNI +VALID_SNI_DOMAIN = "protocol.ai" +VALID_SNI_ADDR = f"/sni/{VALID_SNI_DOMAIN}" + +def basic_sni_usage(): + """ + Basic SNI usage example + + This function demonstrates: + - Creating an SNI multiaddr + - Extracting protocol information + - Extracting the value + - Getting binary representation + """ + + print("=== Basic SNI Usage ===") + print(f"Original SNI: {VALID_SNI_ADDR}") + + try: + ma = Multiaddr(VALID_SNI_ADDR) + print(f"Protocols: {[p.name for p in ma.protocols()]}") + + # Extract the value + sni_value = ma.value_for_protocol('sni') + print(f"Extracted Value: {sni_value}") + + # Get binary representation + binary_data = ma.to_bytes() + print(f"Binary length: {len(binary_data)} bytes") + + print("Valid SNI address: True") + + except Exception as e: + print(f"Error: {e}") + print("Valid SNI address: False") + +def protocol_validation(): + """ + Demonstrate protocol validation. + + This function shows: + - A valid SNI address + - Invalid SNI addresses (invalid domain characters, empty) + - Error handling for validation failures + """ + + print("\n=== Protocol Validation ===") + + # Test valid SNI + print(f"Testing valid SNI: {VALID_SNI_ADDR}") + try: + ma = Multiaddr(VALID_SNI_ADDR) + print(" Valid: True") + print(f" Value: {ma.value_for_protocol('sni')}") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid SNI (invalid characters) + invalid_addr_str = "/sni/invalid_hostname!" + print(f"\nTesting invalid SNI (invalid domain chars): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid SNI (empty value) + invalid_addr_str = "/sni/" + print(f"\nTesting invalid SNI (empty value): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + +def binary_encoding_decoding(): + """ + Demonstrate binary encoding and decoding. + + This function shows: + - Converting multiaddr to binary + - Converting binary back to multiaddr + - Round-trip validation + """ + print("\n=== Binary Encoding/Decoding ===") + + print("SNI binary operations:") + print(f" Original: {VALID_SNI_ADDR}") + + try: + ma = Multiaddr(VALID_SNI_ADDR) + binary_data = ma.to_bytes() + print(f" Binary: {len(binary_data)} bytes") + + # Round-trip: binary back to multiaddr + round_trip_ma = Multiaddr(binary_data) + print(f" Round-trip: {round_trip_ma}") + print(f" Match: {str(ma) == str(round_trip_ma)}") + + except Exception as e: + print(f" Error: {e}") + +def multiaddr_integration(): + """ + Demonstrate SNI protocol integration with other protocols. + + This function shows: + - The most common use case for /sni, nested within /tls. + - Protocol stack analysis + - SNI value extraction + """ + + print("\n=== Multiaddr Integration ===") + + # Create a complex multiaddr with SNI + # This is a typical address for a secure websocket connection + sni_host = "google.com" + complex_addr = f"/dns/google.com/tcp/443/tls/sni/{sni_host}" + + print("Complex multiaddr with SNI:") + print(f" Address: {complex_addr}") + + try: + ma = Multiaddr(complex_addr) + protocols = [p.name for p in ma.protocols()] + print(f" Protocols: {protocols}") + + # Extract SNI value + extracted_sni_value = ma.value_for_protocol('sni') + print(f" Extracted SNI value: {extracted_sni_value}") + print(f" Match: {extracted_sni_value == sni_host}") + + except Exception as e: + print(f" Error: {e}") + +def main(): + """ + Run all SNI protocol examples. + + This function orchestrates all the SNI protocol examples: + 1. Basic SNI usage + 2. Protocol validation + 3. Binary encoding/decoding + 4. Multiaddr integration + + Each example demonstrates different aspects of SNI protocol functionality + and shows how to use it with py-multiaddr. + """ + print("SNI (Server Name Indication) Protocol Examples") + print("=" * 50) + + try: + basic_sni_usage() + protocol_validation() + binary_encoding_decoding() + multiaddr_integration() + + print("\n" + "=" * 50) + print("All examples completed!") + print("\nSummary:") + print("- SNI protocol is working correctly") + print("- Binary encoding/decoding functions properly") + print("- Validation catches invalid domain names") + print("- Integration with /tls protocol works as expected") + + except KeyboardInterrupt: + print("\nExamples interrupted by user") + except Exception as e: + print(f"\nUnexpected error: {e}") + + +if __name__ == "__main__": + main() \ No newline at end of file From 1f51d428ee4cbf67a174716c66292c4d7ccc97ab Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 14:04:40 +0530 Subject: [PATCH 13/17] Added examples for NOISE: examples/noise/noise_examples.py --- examples/noise/noise_examples.py | 236 +++++++++++++++++++++++++++++++ examples/sni/sni_examples.py | 71 +++++----- 2 files changed, 274 insertions(+), 33 deletions(-) create mode 100644 examples/noise/noise_examples.py diff --git a/examples/noise/noise_examples.py b/examples/noise/noise_examples.py new file mode 100644 index 0000000..1f9908c --- /dev/null +++ b/examples/noise/noise_examples.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +""" +Noise protocol examples for py-multiaddr. + +This script demonstrates how to use the 'noise' protocol in py-multiaddr. +The 'noise' protocol is a tag protocol, meaning it does not take an +argument. Its presence signifies that the connection is secured using +the Noise Protocol Framework. + +## Overview + +This script shows various examples of Noise protocol usage: + +1. **Basic Noise Usage**: Creating and parsing a simple /noise address. +2. **Protocol Validation**: Testing valid (/noise) and invalid (/noise/value) + addresses. +3. **Binary Encoding/Decoding**: Working with binary representations. +4. **Multiaddr Integration**: Using 'noise' in a realistic multiaddr string + (e.g., /ip4/127.0.0.1/tcp/12345/noise). +5. **Error Handling**: Demonstrating errors when a value is incorrectly + provided. + +## Expected Output + +When you run this script, you should see output similar to: + +``` +NOISE Protocol Examples +================================================== +=== Basic Noise Usage === +Original Noise: /noise + Protocols: ['noise'] + Binary length: 2 bytes + Valid Noise address: True + +=== Protocol Validation === +Testing valid Noise: /noise + Valid: True + Protocols: ['noise'] + +Testing invalid Noise (with value): /noise/somevalue + Valid: False + Error: Protocol 'noise' does not take an argument + +=== Binary Encoding/Decoding === +Noise binary operations: + Original: /noise + Binary: 2 bytes + Round-trip: /noise Match: True + +=== Multiaddr Integration === +Complex multiaddr with Noise: + Address: /ip4/127.0.0.1/tcp/12345/noise/p2p/Qm... + Protocols: ['ip4', 'tcp', 'noise', 'p2p'] + Has 'noise' protocol: True + +================================================== +All examples completed! +``` + +## Key Features Demonstrated + +- **Noise Protocol**: Represents a connection secured by the Noise framework. +- **Tag Protocol**: 'noise' is a tag-only protocol and takes no arguments. +- **Validation**: Ensures no value is provided to the 'noise' protocol. +- **Binary Operations**: Encoding and decoding to/from binary format. +- **Multiaddr Integration**: Using 'noise' as part of a connection stack. +- **Error Handling**: Proper error handling for invalid formats. + +## Requirements + +- Python 3.10+ +- py-multiaddr library + +## Usage + +```bash +python examples/noise_examples.py +``` +""" + +from multiaddr import Multiaddr + +NOISE_ADDR = "/noise" + +def basic_noise_usage(): + """ + Basic Noise usage example. + + This function demonstrates: + - Creating a Noise multiaddr + - Extracting protocol information + - Getting binary representation + """ + + print(f'Original Noise: {NOISE_ADDR}') + + try: + ma = Multiaddr(NOISE_ADDR) + print(f"Protocols: {[p.name for p in ma.protocols()]}") + + # Get binary representation + binary_data = ma.to_bytes() + print(f"Binary length: {len(binary_data)} bytes") + + print("Valid Noise address: True") + + except Exception as e: + print(f"Error: {e}") + print("Valid Noise address: False") + +def protocol_validation(): + """ + Demonstrate protocol validation. + + This function shows: + - A valid Noise address + - An invalid Noise address (with a value) + - Error handling for validation failures + """ + print("\n=== Protocol Validation ===") + + # Test valid Noise + print(f"Testing valid Noise: {NOISE_ADDR}") + try: + ma = Multiaddr(NOISE_ADDR) + print(" Valid: True") + print(f" Protocols: {[p.name for p in ma.protocols()]}") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid Noise (with a value) + invalid_addr_str = "/noise/somevalue" + print(f"\nTesting invalid Noise (with value): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + +def binary_encoding_decoding(): + """ + Demonstrate binary encoding and decoding. + + This function shows: + - Converting multiaddr to binary + - Converting binary back to multiaddr + - Round-trip validation + """ + print("\n=== Binary Encoding/Decoding ===") + + print("Noise binary operations:") + print(f" Original: {NOISE_ADDR}") + + try: + ma = Multiaddr(NOISE_ADDR) + binary_data = ma.to_bytes() + print(f" Binary: {len(binary_data)} bytes") + + # Round-trip: binary back to multiaddr + round_trip_ma = Multiaddr(binary_data) + print(f" Round-trip: {round_trip_ma}") + print(f" Match: {str(ma) == str(round_trip_ma)}") + + except Exception as e: + print(f" Error: {e}") + +def multiaddr_integration(): + """ + Demonstrate Noise protocol integration with other protocols. + + This function shows: + - Using /noise as part of a libp2p stack. + - Protocol stack analysis + """ + print("\n=== Multiaddr Integration ===") + + # Create a complex multiaddr with Noise + # This is a typical address for a libp2p node + peer_id = "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN" + complex_addr = f"/ip4/127.0.0.1/tcp/12345/noise/p2p/{peer_id}" + + print("Complex multiaddr with Noise:") + print(f" Address: {complex_addr}") + + try: + ma = Multiaddr(complex_addr) + protocols = [p.name for p in ma.protocols()] + print(f" Protocols: {protocols}") + + # Check for 'noise' protocol + has_noise = 'noise' in protocols + print(f" Has 'noise' protocol: {has_noise}") + + except Exception as e: + print(f" Error: {e}") + +def main(): + """ + Run all Noise protocol examples. + + This function orchestrates all the Noise protocol examples: + 1. Basic Noise usage + 2. Protocol validation + 3. Binary encoding/decoding + 4. Multiaddr integration + + Each example demonstrates different aspects of Noise protocol functionality + and shows how to use it with py-multiaddr. + """ + print("Noise Protocol Examples") + print("=" * 50) + + try: + basic_noise_usage() + protocol_validation() + binary_encoding_decoding() + multiaddr_integration() + + print("\n" + "=" * 50) + print("All examples completed!") + print("\nSummary:") + print("- Noise protocol is working correctly") + print("- Binary encoding/decoding functions properly") + print("- Validation catches invalid use (with a value)") + print("- Integration with libp2p stack works as expected") + + except KeyboardInterrupt: + print("\nExamples interrupted by user") + except Exception as e: + print(f"\nUnexpected error: {e}") + +if __name__ == "__main__": + main() diff --git a/examples/sni/sni_examples.py b/examples/sni/sni_examples.py index d75d06a..e0524b7 100644 --- a/examples/sni/sni_examples.py +++ b/examples/sni/sni_examples.py @@ -12,7 +12,7 @@ 1. **Basic SNI Usage**: Creating and parsing a basic /sni/hostname address. 2. **Protocol Validation**: Testing valid and invalid hostnames. 3. **Binary Encoding/Decoding**: Working with binary representations. -4. **Multiaddr Integration**: Using 'sni' in a realistic multiaddr string +4. **Multiaddr Integration**: Using 'sni' in a realistic multiaddr string (e.g., /dns/google.com/tcp/443/tls/sni/google.com). 5. **Error Handling**: Demonstrating errors for invalid domain names. @@ -24,34 +24,34 @@ SNI Protocol Examples ================================================== === Basic SNI Usage === -Original SNI: /sni/protocol.ai -Protocols: ['sni'] -Extracted Value: protocol.ai -Binary length: 14 bytes -Valid SNI address: True +Original SNI: /sni/protocol.ai + Protocols: ['sni'] + Extracted Value: protocol.ai + Binary length: 14 bytes + Valid SNI address: True === Protocol Validation === -Testing valid SNI: /sni/protocol.ai +Testing valid SNI: /sni/protocol.ai Valid: True Value: protocol.ai Testing invalid SNI (invalid domain chars): /sni/invalid_hostname! Valid: False Error: invalid_hostname! is not a valid domain name -Testing invalid SNI (empty value): /sni/ +Testing invalid SNI (empty value): /sni/ Valid: False Error: is not a valid domain name - + === Binary Encoding/Decoding === -SNI binary operations: +SNI binary operations: Original: /sni/protocol.ai - Binary: 14 bytes - Round-trip: /sni/protocol.ai + Binary: 14 bytes + Round-trip: /sni/protocol.ai Match: True -=== Multiaddr Integration === -Complex multiaddr with SNI: - Address: /dns/google.com/tcp/443/tls/sni/google.com - Protocols: ['dns', 'tcp', 'tls', 'sni'] +=== Multiaddr Integration === +Complex multiaddr with SNI: + Address: /dns/google.com/tcp/443/tls/sni/google.com + Protocols: ['dns', 'tcp', 'tls', 'sni'] Extracted SNI value: google.com Match: True @@ -85,38 +85,40 @@ VALID_SNI_DOMAIN = "protocol.ai" VALID_SNI_ADDR = f"/sni/{VALID_SNI_DOMAIN}" + def basic_sni_usage(): """ Basic SNI usage example - + This function demonstrates: - Creating an SNI multiaddr - Extracting protocol information - Extracting the value - Getting binary representation """ - + print("=== Basic SNI Usage ===") print(f"Original SNI: {VALID_SNI_ADDR}") - + try: ma = Multiaddr(VALID_SNI_ADDR) print(f"Protocols: {[p.name for p in ma.protocols()]}") - + # Extract the value - sni_value = ma.value_for_protocol('sni') + sni_value = ma.value_for_protocol("sni") print(f"Extracted Value: {sni_value}") - + # Get binary representation binary_data = ma.to_bytes() print(f"Binary length: {len(binary_data)} bytes") - + print("Valid SNI address: True") - + except Exception as e: print(f"Error: {e}") print("Valid SNI address: False") - + + def protocol_validation(): """ Demonstrate protocol validation. @@ -126,7 +128,7 @@ def protocol_validation(): - Invalid SNI addresses (invalid domain characters, empty) - Error handling for validation failures """ - + print("\n=== Protocol Validation ===") # Test valid SNI @@ -158,7 +160,8 @@ def protocol_validation(): except Exception as e: print(" Valid: False") print(f" Error: {e}") - + + def binary_encoding_decoding(): """ Demonstrate binary encoding and decoding. @@ -185,7 +188,8 @@ def binary_encoding_decoding(): except Exception as e: print(f" Error: {e}") - + + def multiaddr_integration(): """ Demonstrate SNI protocol integration with other protocols. @@ -195,7 +199,7 @@ def multiaddr_integration(): - Protocol stack analysis - SNI value extraction """ - + print("\n=== Multiaddr Integration ===") # Create a complex multiaddr with SNI @@ -212,13 +216,14 @@ def multiaddr_integration(): print(f" Protocols: {protocols}") # Extract SNI value - extracted_sni_value = ma.value_for_protocol('sni') + extracted_sni_value = ma.value_for_protocol("sni") print(f" Extracted SNI value: {extracted_sni_value}") print(f" Match: {extracted_sni_value == sni_host}") except Exception as e: print(f" Error: {e}") + def main(): """ Run all SNI protocol examples. @@ -253,7 +258,7 @@ def main(): print("\nExamples interrupted by user") except Exception as e: print(f"\nUnexpected error: {e}") - - + + if __name__ == "__main__": - main() \ No newline at end of file + main() From eabec3ccf62feefa4d55e1d4bc4ce8bc7df43845 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 14:05:22 +0530 Subject: [PATCH 14/17] Fix linter errors --- examples/noise/noise_examples.py | 48 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/examples/noise/noise_examples.py b/examples/noise/noise_examples.py index 1f9908c..17f7973 100644 --- a/examples/noise/noise_examples.py +++ b/examples/noise/noise_examples.py @@ -15,7 +15,7 @@ 2. **Protocol Validation**: Testing valid (/noise) and invalid (/noise/value) addresses. 3. **Binary Encoding/Decoding**: Working with binary representations. -4. **Multiaddr Integration**: Using 'noise' in a realistic multiaddr string +4. **Multiaddr Integration**: Using 'noise' in a realistic multiaddr string (e.g., /ip4/127.0.0.1/tcp/12345/noise). 5. **Error Handling**: Demonstrating errors when a value is incorrectly provided. @@ -29,7 +29,7 @@ ================================================== === Basic Noise Usage === Original Noise: /noise - Protocols: ['noise'] + Protocols: ['noise'] Binary length: 2 bytes Valid Noise address: True @@ -38,22 +38,22 @@ Valid: True Protocols: ['noise'] -Testing invalid Noise (with value): /noise/somevalue - Valid: False +Testing invalid Noise (with value): /noise/somevalue + Valid: False Error: Protocol 'noise' does not take an argument === Binary Encoding/Decoding === -Noise binary operations: - Original: /noise - Binary: 2 bytes +Noise binary operations: + Original: /noise + Binary: 2 bytes Round-trip: /noise Match: True === Multiaddr Integration === Complex multiaddr with Noise: - Address: /ip4/127.0.0.1/tcp/12345/noise/p2p/Qm... - Protocols: ['ip4', 'tcp', 'noise', 'p2p'] + Address: /ip4/127.0.0.1/tcp/12345/noise/p2p/Qm... + Protocols: ['ip4', 'tcp', 'noise', 'p2p'] Has 'noise' protocol: True - + ================================================== All examples completed! ``` @@ -83,6 +83,7 @@ NOISE_ADDR = "/noise" + def basic_noise_usage(): """ Basic Noise usage example. @@ -92,9 +93,9 @@ def basic_noise_usage(): - Extracting protocol information - Getting binary representation """ - - print(f'Original Noise: {NOISE_ADDR}') - + + print(f"Original Noise: {NOISE_ADDR}") + try: ma = Multiaddr(NOISE_ADDR) print(f"Protocols: {[p.name for p in ma.protocols()]}") @@ -109,7 +110,8 @@ def basic_noise_usage(): print(f"Error: {e}") print("Valid Noise address: False") -def protocol_validation(): + +def protocol_validation(): """ Demonstrate protocol validation. @@ -140,7 +142,8 @@ def protocol_validation(): print(" Valid: False") print(f" Error: {e}") -def binary_encoding_decoding(): + +def binary_encoding_decoding(): """ Demonstrate binary encoding and decoding. @@ -166,9 +169,10 @@ def binary_encoding_decoding(): except Exception as e: print(f" Error: {e}") - -def multiaddr_integration(): - """ + + +def multiaddr_integration(): + """ Demonstrate Noise protocol integration with other protocols. This function shows: @@ -191,12 +195,13 @@ def multiaddr_integration(): print(f" Protocols: {protocols}") # Check for 'noise' protocol - has_noise = 'noise' in protocols + has_noise = "noise" in protocols print(f" Has 'noise' protocol: {has_noise}") except Exception as e: print(f" Error: {e}") - + + def main(): """ Run all Noise protocol examples. @@ -231,6 +236,7 @@ def main(): print("\nExamples interrupted by user") except Exception as e: print(f"\nUnexpected error: {e}") - + + if __name__ == "__main__": main() From eabc3bb3c9bdd8c011218cf2586b137ae059a61e Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 14:23:57 +0530 Subject: [PATCH 15/17] Added examples for certhash: examples/certhash/certhash_examples.py --- examples/certhash/certhash_examples.py | 268 +++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 examples/certhash/certhash_examples.py diff --git a/examples/certhash/certhash_examples.py b/examples/certhash/certhash_examples.py new file mode 100644 index 0000000..bbb6821 --- /dev/null +++ b/examples/certhash/certhash_examples.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +""" +Certhash protocol examples for py-multiaddr. + +This script demonstrates how to use the 'certhash' (Certificate Hash) +protocol in py-multiaddr. This protocol is used to pin a specific +peer certificate, typically for protocols like 'webrtc-direct'. + +The value is a multibase-encoded multihash of the certificate's +SubjectPublicKeyInfo (SPKI). + +## Overview + +This script shows various examples of Certhash protocol usage: + +1. **Basic Certhash Usage**: Creating and parsing a /certhash/... address. +2. **Protocol Validation**: Testing valid and invalid certhash values. +3. **Binary Encoding/Decoding**: Working with binary representations. +4. **Multiaddr Integration**: Using 'certhash' in a realistic multiaddr string + (e.g., /ip4/.../webrtc-direct/certhash/...). +5. **Error Handling**: Demonstrating errors for invalid multibase or + multihash encodings. + +## Expected Output + +When you run this script, you should see output similar to: + +``` +CERTHASH Protocol Examples +================================================== +=== Basic Certhash Usage === +Original Certhash: /certhash/uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c + Protocols: ['certhash'] + Extracted Value: uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c + Binary length: 37 bytes + Valid Certhash address: True + +=== Protocol Validation === +Testing valid Certhash: /certhash/uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c +Valid: True +Value: uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c + +Testing invalid Certhash (invalid multibase prefix): /certhash/!EiAs... +Valid: False +Error: Unsupported multibase encoding: ! + +Testing invalid Certhash (not a multihash): /certhash/uYXNkZg +Valid: False +Error: multihash length too short. must be >= 2 + +=== Binary Encoding/Decoding === +Certhash binary operations: +Original: /certhash/uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c + Binary: 37 bytes + Round-trip: /certhash/uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c + Match: True + +=== Multiaddr Integration === +Complex multiaddr with Certhash: +Address: /ip4/127.0.0.1/udp/9090/webrtc-direct/certhash/uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c +Protocols: ['ip4', 'udp', 'webrtc-direct', 'certhash'] +Extracted Certhash value: uEiAsGPzpiN3E4i94n924-T1wA0-z0a9b-Gtc-E-8a-c +Match: True + +================================================== +All examples completed! +``` + +## Key Features Demonstrated + +- **Certhash Protocol**: Represents a certificate hash. +- **Validation**: Ensures the value is a valid multibase-encoded multihash. +- **Binary Operations**: Encoding and decoding to/from binary format. +- **Multiaddr Integration**: Using 'certhash' with 'webrtc-direct'. +- **Error Handling**: Proper error handling for invalid encodings. + +## Requirements + +- Python 3.10+ +- py-multiaddr library (which uses py-multihash and py-multibase) + +## Usage + +```bash +python examples/certhash_examples.py +``` +""" + +from multiaddr import Multiaddr + +VALID_CERTHASH_VALUE = "uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g" +VALID_CERTHASH_ADDR = f"/certhash/{VALID_CERTHASH_VALUE}" + + +def basic_certhash_usage(): + """ + Basic Certhash usage example. + + This function demonstrates: + - Creating a Certhash multiaddr + - Extracting protocol information + - Extracting the value + - Getting binary representation + """ + print("=== Basic Certhash Usage ===") + + print(f"Original Certhash: {VALID_CERTHASH_ADDR}") + + try: + ma = Multiaddr(VALID_CERTHASH_ADDR) + print(f"Protocols: {[p.name for p in ma.protocols()]}") + + # Extract the value + certhash_value = ma.value_for_protocol("certhash") + print(f"Extracted Value: {certhash_value}") + + # Get binary representation + binary_data = ma.to_bytes() + print(f"Binary length: {len(binary_data)} bytes") + + print("Valid Certhash address: True") + + except Exception as e: + print(f"Error: {e}") + print("Valid Certhash address: False") + + +def protocol_validation(): + """ + Demonstrate protocol validation. + + This function shows: + - A valid Certhash address + - Invalid Certhash addresses (invalid multibase, not a multihash) + - Error handling for validation failures + """ + print("\n=== Protocol Validation ===") + + # Test valid Certhash + print(f"Testing valid Certhash: {VALID_CERTHASH_ADDR}") + try: + ma = Multiaddr(VALID_CERTHASH_ADDR) + print(" Valid: True") + print(f" Value: {ma.value_for_protocol('certhash')}") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid Certhash (invalid multibase prefix) + invalid_addr_str = "/certhash/!EiAs..." + print(f"\nTesting invalid Certhash (invalid multibase prefix): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid Certhash (valid multibase, but not a multihash) + # 'uYXNkZg' is 'asdf' in base64url. This is too short to be a multihash. + invalid_addr_str = "/certhash/uYXNkZg" + print(f"\nTesting invalid Certhash (not a multihash): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + +def binary_encoding_decoding(): + """ + Demonstrate binary encoding and decoding. + + This function shows: + - Converting multiaddr to binary + - Converting binary back to multiaddr + - Round-trip validation + """ + print("\n=== Binary Encoding/Decoding ===") + + print("Certhash binary operations:") + print(f" Original: {VALID_CERTHASH_ADDR}") + + try: + ma = Multiaddr(VALID_CERTHASH_ADDR) + binary_data = ma.to_bytes() + print(f" Binary: {len(binary_data)} bytes") + + # Round-trip: binary back to multiaddr + round_trip_ma = Multiaddr(binary_data) + print(f" Round-trip: {round_trip_ma}") + print(f" Match: {str(ma) == str(round_trip_ma)}") + + except Exception as e: + print(f" Error: {e}") + + +def multiaddr_integration(): + """ + Demonstrate Certhash protocol integration with other protocols. + + This function shows: + - The most common use case for /certhash, nested within /webrtc-direct. + - Protocol stack analysis + - Certhash value extraction + """ + print("\n=== Multiaddr Integration ===") + + # Create a complex multiaddr with Certhash + # This is a typical address for a WebRTC direct connection + complex_addr = f"/ip4/127.0.0.1/udp/9090/webrtc-direct/certhash/{VALID_CERTHASH_VALUE}" + + print("Complex multiaddr with Certhash:") + print(f" Address: {complex_addr}") + + try: + ma = Multiaddr(complex_addr) + protocols = [p.name for p in ma.protocols()] + print(f" Protocols: {protocols}") + + # Extract Certhash value + extracted_certhash_value = ma.value_for_protocol("certhash") + print(f" Extracted Certhash value: {extracted_certhash_value}") + print(f" Match: {extracted_certhash_value == VALID_CERTHASH_VALUE}") + + except Exception as e: + print(f" Error: {e}") + + +def main(): + """ + Run all Certhash protocol examples. + + This function orchestrates all the Certhash protocol examples: + 1. Basic Certhash usage + 2. Protocol validation + 3. Binary encoding/decoding + 4. Multiaddr integration + + Each example demonstrates different aspects of Certhash protocol + functionality and shows how to use it with py-multiaddr. + """ + print("Certhash Protocol Examples") + print("=" * 50) + + try: + basic_certhash_usage() + protocol_validation() + binary_encoding_decoding() + multiaddr_integration() + + print("\n" + "=" * 50) + print("All examples completed!") + print("\nSummary:") + print("- Certhash protocol is working correctly") + print("- Binary encoding/decoding functions properly") + print("- Validation catches invalid multibase/multihash values") + print("- Integration with /webrtc-direct works as expected") + + except KeyboardInterrupt: + print("\nExamples interrupted by user") + except Exception as e: + print(f"\nUnexpected error: {e}") + + +if __name__ == "__main__": + main() From c0f095ae5ce2ce3682dadfdd0e460c8087201e1b Mon Sep 17 00:00:00 2001 From: lla-dane Date: Fri, 24 Oct 2025 14:38:52 +0530 Subject: [PATCH 16/17] Added examples for webrtc/-direct: examples/webrtc/webrtc_examples.py --- examples/webrtc/webrtc_examples.py | 332 +++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 examples/webrtc/webrtc_examples.py diff --git a/examples/webrtc/webrtc_examples.py b/examples/webrtc/webrtc_examples.py new file mode 100644 index 0000000..65d41f3 --- /dev/null +++ b/examples/webrtc/webrtc_examples.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +""" +WebRTC and WebRTC-Direct protocol examples for py-multiaddr. + +This script demonstrates how to use the 'webrtc' and 'webrtc-direct' +protocols in py-multiaddr. Both are "tag" protocols that take no arguments. + +## Overview + +This script shows various examples of WebRTC protocol usage: + +1. **Basic `webrtc-direct` Usage**: Creating and parsing /webrtc-direct. +2. **Basic `webrtc` Usage**: Creating and parsing /webrtc. +3. **Protocol Validation**: Testing valid (tag-only) and invalid (with value) + addresses for both protocols. +4. **Binary Encoding/Decoding**: Working with binary representations. +5. **Multiaddr Integration**: + - Using 'webrtc-direct' in a peer-to-peer stack. + - Using 'webrtc' in a relayed (p2p-circuit) stack. +6. **Error Handling**: Demonstrating errors when a value is incorrectly + provided. + +## Expected Output + +When you run this script, you should see output similar to: + +``` +WebRTC/WebRTC-Direct Protocol Examples +================================================== + +=== Basic webrtc-direct Usage === +Original: /webrtc-direct + Protocols: ['webrtc-direct'] + Binary length: 2 bytes + Valid address: True + +=== Basic webrtc Usage === +Original: /webrtc + Protocols: ['webrtc'] + Binary length: 2 bytes + Valid address: True + +=== Protocol Validation === +Testing valid /webrtc-direct: + Valid: True + Protocols: ['webrtc-direct'] + +Testing invalid /webrtc-direct (with value): /webrtc-direct/value + Valid: False + Error: Protocol 'webrtc-direct' does not take an argument + +Testing valid /webrtc: + Valid: True + Protocols: ['webrtc'] + +Testing invalid /webrtc (with value): /webrtc/value + Valid: False + Error: Protocol 'webrtc' does not take an argument + +=== Binary Encoding/Decoding === +webrtc-direct binary operations: +Original: /webrtc-direct + Binary: 2 bytes + Round-trip: /webrtc-direct + Match: True + +webrtc binary operations: +Original: /webrtc + Binary: 2 bytes + Round-trip: /webrtc + Match: True + +=== Multiaddr Integration === +Complex multiaddr with webrtc-direct: + Address: /ip4/1.2.3.4/udp/9090/webrtc-direct/certhash/uEiAsGP... + Protocols: ['ip4', 'udp', 'webrtc-direct', '`certhash`'] + Has 'webrtc-direct' protocol: True + +Complex multiaddr with webrtc (relayed): + Address: /dns4/relay.com/tcp/443/wss/p2p/QmRelay/p2p-circuit/webrtc + Protocols: ['dns4', 'tcp', 'wss', 'p2p', 'p2p-circuit', 'webrtc'] + Has 'webrtc' protocol: True + +================================================== +All examples completed! +``` +## Key Features Demonstrated + +- **`webrtc-direct` Protocol**: Represents a direct, peer-to-peer WebRTC + connection. +- **`webrtc` Protocol**: Represents a relayed WebRTC connection. +- **Tag Protocols**: Both are tag-only and take no arguments. +- **Validation**: Ensures no value is provided to either protocol. +- **Binary Operations**: Encoding and decoding to/from binary format. +- **Multiaddr Integration**: Using both protocols in realistic stacks. +- **Error Handling**: Proper error handling for invalid formats. + +## Requirements + +- Python 3.10+ +- py-multiaddr library + +## Usage + +```bash +python examples/webrtc_examples.py +``` +""" + +from multiaddr import Multiaddr + +WEBRTC_DIRECT_ADDR = "/webrtc-direct" +WEBRTC_ADDR = "/webrtc" + + +def basic_webrtc_direct_usage(): + """ + Basic webrtc-direct usage example. + + This function demonstrates: + - Creating a `webrtc-direct` multiaddr + - Extracting protocol information + - Getting binary representation + """ + print("=== Basic `webrtc-direct` Usage ===") + print(f"Original: {WEBRTC_DIRECT_ADDR}") + + try: + ma = Multiaddr(WEBRTC_DIRECT_ADDR) + print(f"Protocols: {[p.name for p in ma.protocols()]}") + + # Get binary representation + binary_data = ma.to_bytes() + print(f"Binary length: {len(binary_data)} bytes") + + print("Valid address: True") + + except Exception as e: + print(f"Error: {e}") + print("Valid address: False") + + +def basic_webrtc_usage(): + """ + Basic webrtc usage example. + + This function demonstrates: + - Creating a `webrtc` multiaddr + - Extracting protocol information + - Getting binary representation + """ + print("\n=== Basic `webrtc` Usage ===") + print(f"Original: {WEBRTC_ADDR}") + + try: + ma = Multiaddr(WEBRTC_ADDR) + print(f"Protocols: {[p.name for p in ma.protocols()]}") + + # Get binary representation + binary_data = ma.to_bytes() + print(f"Binary length: {len(binary_data)} bytes") + + print("Valid address: True") + + except Exception as e: + print(f"Error: {e}") + print("Valid address: False") + + +def protocol_validation(): + """ + Demonstrate protocol validation for both protocols. + + This function shows: + - Valid tag-only addresses + - Invalid addresses (with a value) + - Error handling for validation failures + """ + print("\n=== Protocol Validation ===") + + # Test valid `webrtc-direct` + print("Testing valid /webrtc-direct:") + try: + ma = Multiaddr(WEBRTC_DIRECT_ADDR) + print(" Valid: True") + print(f" Protocols: {[p.name for p in ma.protocols()]}") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid `webrtc-direct` (with a value) + invalid_addr_str = "/webrtc-direct/value" + print(f"Testing invalid /webrtc-direct (with value): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test valid `webrtc` + print("Testing valid /webrtc:") + try: + ma = Multiaddr(WEBRTC_ADDR) + print(" Valid: True") + print(f" Protocols: {[p.name for p in ma.protocols()]}") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + # Test invalid `webrtc` (with a value) + invalid_addr_str = "/webrtc/value" + print(f"Testing invalid /webrtc (with value): {invalid_addr_str}") + try: + Multiaddr(invalid_addr_str) + print(" Valid: True (ERROR: Should have failed)") + except Exception as e: + print(" Valid: False") + print(f" Error: {e}") + + +def binary_encoding_decoding(): + """ + Demonstrate binary encoding and decoding for both protocols. + + This function shows: + - Converting multiaddr to binary + - Converting binary back to multiaddr + - Round-trip validation + """ + print("\n=== Binary Encoding/Decoding ===") + + print("`webrtc-direct` binary operations:") + try: + ma = Multiaddr(WEBRTC_DIRECT_ADDR) + binary_data = ma.to_bytes() + print(f" Original: {WEBRTC_DIRECT_ADDR}") + print(f" Binary: {len(binary_data)} bytes") + round_trip_ma = Multiaddr(binary_data) + print(f" Round-trip: {round_trip_ma}") + print(f" Match: {str(ma) == str(round_trip_ma)}") + except Exception as e: + print(f" Error: {e}") + + print("`webrtc` binary operations:") + try: + ma = Multiaddr(WEBRTC_ADDR) + binary_data = ma.to_bytes() + print(f" Original: {WEBRTC_ADDR}") + print(f" Binary: {len(binary_data)} bytes") + round_trip_ma = Multiaddr(binary_data) + print(f" Round-trip: {round_trip_ma}") + print(f" Match: {str(ma) == str(round_trip_ma)}") + except Exception as e: + print(f" Error: {e}") + + +def multiaddr_integration(): + """ + Demonstrate WebRTC protocol integration with other protocols. + + This function shows: + - Using /webrtc-direct in a P2P stack with /certhash. + - Using /webrtc in a relayed (p2p-circuit) stack. + """ + print("\n=== Multiaddr Integration ===") + + # Example 1: `webrtc-direct` + # A typical address for a direct peer-to-peer WebRTC connection + certhash = "uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g" + complex_addr_direct = f"/ip4/1.2.3.4/udp/9090/webrtc-direct/certhash/{certhash}" + + print("Complex multiaddr with `webrtc-direct`:") + print(f" Address: /ip4/1.2.3.4/udp/9090/webrtc-direct/certhash/{certhash}") + + try: + ma = Multiaddr(complex_addr_direct) + protocols = [p.name for p in ma.protocols()] + print(f" Protocols: {protocols}") + has_protocol = "webrtc-direct" in protocols + print(f" Has 'webrtc-direct' protocol: {has_protocol}") + except Exception as e: + print(f" Error: {e}") + + # Example 2: `webrtc` (relayed) + # A typical address for a browser connecting to a peer via a relay + relay_id = "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN" + complex_addr_relayed = f"/dns4/relay.com/tcp/443/wss/p2p/{relay_id}/p2p-circuit/webrtc" + + print("\nComplex multiaddr with `webrtc` (relayed):") + print(f" Address: {complex_addr_relayed}") + + try: + ma = Multiaddr(complex_addr_relayed) + protocols = [p.name for p in ma.protocols()] + print(f" Protocols: {protocols}") + has_protocol = "webrtc" in protocols + print(f" Has 'webrtc' protocol: {has_protocol}") + except Exception as e: + print(f" Error: {e}") + + +def main(): + """ + Run all WebRTC and WebRTC-Direct protocol examples. + """ + print("WebRTC / WebRTC-Direct Protocol Examples") + print("=" * 50) + + try: + basic_webrtc_direct_usage() + basic_webrtc_usage() + protocol_validation() + binary_encoding_decoding() + multiaddr_integration() + + print("\n" + "=" * 50) + print("All examples completed!") + print("\nSummary:") + print("- `webrtc-direct` and `webrtc` protocols are working") + print("- Binary encoding/decoding functions properly") + print("- Validation catches invalid use (with a value)") + print("- Integration with P2P stacks works as expected") + + except KeyboardInterrupt: + print("\nExamples interrupted by user") + except Exception as e: + print(f"\nUnexpected error: {e}") + + +if __name__ == "__main__": + main() From 88a69a0bade242fd45790893fe21f4806457b6e7 Mon Sep 17 00:00:00 2001 From: lla-dane Date: Sun, 26 Oct 2025 14:59:29 +0530 Subject: [PATCH 17/17] Add certhash addrs in valid tests also --- tests/test_multiaddr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_multiaddr.py b/tests/test_multiaddr.py index 35cb74a..e54bcc1 100644 --- a/tests/test_multiaddr.py +++ b/tests/test_multiaddr.py @@ -140,6 +140,8 @@ def test_invalid(addr_str): "/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct", "/ip4/127.0.0.1/tcp/127/webrtc-direct", "/ip4/127.0.0.1/tcp/127/webrtc", + "/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g" + "/ip4/127.0.0.1/udp/9090/webrtc-direct/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g", ], ) # nopep8 def test_valid(addr_str):