Repository navigation
py-multiaddr ↔ go-multiaddr Feature Parity Analysis #1364
sumanjeet0012
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
py-multiaddr Feature Parity Analysis
Table of Contents
net/manet)x/meg)manetequivalent)Executive Summary
The Go reference implementation (
go-multiaddr) is significantly more complete than the Python implementation (py-multiaddr). While Python has a solid foundation for parsing, encoding, and basic manipulation of multiaddrs, Go provides an entire network stack (net/manet), IP classification utilities, address filtering, pattern matching, and a CLI tool — none of which exist in Python.SplitFirst,SplitLast,SplitFunc)Componenttype; Python treats as flat bytesCompare()/ orderingCast()/StringCast()FilterAddrs,Contains,Unique)net.Addr↔ Multiaddr conversionFilters)plaintextv2get_thin_waist_addresses)Architecture Comparison
Go Architecture (Rich, Network-Integrated)
Python Architecture (Solid Core, No Network Layer)
Feature-by-Feature Gap Analysis
3.1 Core Multiaddr Type
NewMultiaddr(s)Multiaddr(s)NewMultiaddrBytes(b)Multiaddr(b)Multiaddr(other_ma).String()str(ma).Bytes().to_bytes()/bytes(ma).Equal(m2)ma1 == ma2__hash__.Compare(o) int.Protocols() []Protocol.protocols()→MultiAddrKeys.ValueForProtocol(code).value_for_protocol(proto)/ma[proto]len(m)(native slice)len(ma)for _, c := range mfor proto in maMultiAddrKeys,MultiAddrItems,MultiAddrValuesnil+ errorMultiaddr("")3.2 Component Model
ComponenttypeComponent.Bytes()Component.String()Component.Value()Component.RawValue()Component.Protocol()Component.Code()Component.Equal()/Compare()NewComponent(protocol, value)3.3 Protocol Registry
ProtocolstructProtocolWithName(s)protocol_with_name(name)ProtocolWithCode(c)protocol_with_code(code)ProtocolsWithString(s)protocols_with_string(s)AddProtocol(p)registry.add(proto)registry.lock()registry.copy(unlock=True)"ipfs"→"p2p")add_alias_name()add_alias_code()3.4 Encapsulate / Decapsulate
Encapsulate(other)encapsulate(other)Decapsulate(right)decapsulate(addr)(string-based rindex)DecapsulateCode(code)decapsulate_code(code)Join(ms...)variadicMultiaddr.join(*addrs)3.5 Split / Join Utilities
Split(m)→ components[]Componentsplit(maxsplit)returnslist[Multiaddr]SplitFirst(m)→ (Component, Multiaddr)SplitLast(m)→ (Multiaddr, Component)SplitFunc(m, cb)→ (Multiaddr, Multiaddr)ForEach(m, cb)forloop)Cast(b)(panic on error)StringCast(s)(panic on error)3.6 Serialization (JSON / Binary / Text)
MarshalJSON/UnmarshalJSONMarshalBinary/UnmarshalBinaryMarshalText/UnmarshalTextGo's
Multiaddrcan be directly serialized/deserialized viaencoding/json,encoding/binary, andencoding.TextMarshaler. Python has none of these interfaces.3.7 Collection Utilities (Filter, Contains, Unique)
FilterAddrs(addrs, ...filters)Contains(addrs, addr)Unique(addrs)3.8 Network Layer (
net/manet)This is the largest gap between the two implementations. Go's
netsub-package provides a complete network integration layer.manetDial(remote)→ConnDialContext(ctx, remote)→ConnListen(laddr)→ListenerListenPacket(laddr)→PacketConnConninterface (LocalMultiaddr, RemoteMultiaddr)Listenerinterface (Accept, Close, Multiaddr)PacketConninterfaceDialerstructWrapNetConn(nconn)WrapNetListener(nl)WrapPacketConn(pc)NetListener(l)→net.ListenerInterfaceMultiaddrs()AddrMatch(match, addrs)FromNetAddr(a)→ MultiaddrToNetAddr(ma)→net.AddrFromIP(ip)→ MultiaddrToIP(ma)→net.IPFromIPAndZone(ip, zone)DialArgs(ma)→ (network, address)MultiaddrToIPNet(ma)→*net.IPNetCodecMap(extensible conversion registry)RegisterFromNetAddr/RegisterToNetAddr3.9 IP Classification Utilities
IsThinWaist(ma)get_thin_waist_addresses)IsIPLoopback(ma)IsIP6LinkLocal(ma)is_link_local_ip()(string-based, in utils.py)IsIPUnspecified(ma)is_wildcard()(string-based)IsNAT64IPv4ConvertedIPv6Addr(ma)IsPublicAddr(ma)IsPrivateAddr(ma)IP4Loopback,IP6Loopback,IP4Unspecified, etc.)Private4/6,Unroutable4/63.10 Address Filtering
FiltersstructNewFilters()AddFilter(ipnet, action)RemoveLiteral(ipnet)AddrBlocked(ma)ActionForFilter(ipnet)FiltersForAction(action)DefaultAction3.11 DNS Resolution
/dns,/dns4,/dns6DNSResolverDNSResolver._resolve_dnsaddr()MAX_RECURSIVE_DEPTH = 32ResolveUnspecifiedAddress(expand wildcards)get_thin_waist_addresses()(different approach)3.12 Pattern Matching (
x/meg)Pattern,MatchState,MatchabletypesMultiaddr.Match(patterns...)3.13 CLI Tool
multiaddr0x...)3.14 Protocol Coverage
3.15 Error Handling
fmt.Errorf("failed to parse multiaddr...")StringParseErrorfmt.Errorf("no protocol with code...")BinaryParseErrorErrProtocolNotFoundProtocolLookupErrorfmt.Errorf("protocol by the name...already exists")ProtocolExistsErrorProtocolRegistryLockedProtocolNotFoundErrorResolutionError/RecursionLimitErrorMultiaddrError(base)Python has a richer exception hierarchy.
3.16 Testing
Feature Matrix Summary
Componenttype (individual address part)NewComponent(protocol, value)Multiaddr.Compare()(ordering)MarshalJSON/UnmarshalJSONMarshalBinary/UnmarshalBinaryMarshalText/UnmarshalTextCast(b)/StringCast(s)(panic-on-error)SplitFirst(m)/SplitLast(m)/SplitFunc(m, cb)ForEach(m, cb)FilterAddrs(addrs, ...filters)Contains(addrs, addr)Unique(addrs)(deduplicate)Dial,Listen,Conn,ListenerFromNetAddr/ToNetAddrFromIP/ToIPDialArgs(ma)InterfaceMultiaddrs()CodecMap(extensible registry)IsThinWaist(ma)IsIPLoopback(ma)IsIPUnspecified(ma)IsPublicAddr(ma)/IsPrivateAddr(ma)IsIP6LinkLocal(ma)IsNAT64IPv4ConvertedIPv6Addr(ma)IP4Loopback, etc.)Filters(Accept/Deny IP ranges)x/megregex for multiaddrs)plaintextv2MultiaddrToIPNet(ma)ResolveUnspecifiedAddress(es)Phased Roadmap
Phase 1 – Core API Gaps & Serialization
Goal: Add the most impactful missing core APIs and serialization support.
Estimated effort: 2–3 weeks
1.1 Add
ComponentclassCreate a
Componenttype that represents a single protocol + value pair:1.2 Refactor
Multiaddraslist[Component]Optionally, refactor the internal representation from
bytestolist[Component](matching Go's[]Component). This would enable efficient component-level access. Alternatively, add lazy component parsing.1.3 Add
Compare()method1.4 Add JSON/Binary/Text serialization
1.5 Add
SplitFirst,SplitLast,SplitFunc1.6 Add
Cast()andStringCast()1.7 Add
plaintextv2protocolPhase 2 – Network Layer (
manetequivalent)Goal: Create a
multiaddr.netsub-package providing Python equivalents of Go'smanet.Estimated effort: 3–4 weeks
2.1 Create
multiaddr/net/__init__.py2.2 Implement
dial_args(ma) → (network, address)2.3 Implement
from_ip(ip) → Multiaddrandto_ip(ma) → ipaddress.IPv4Address | IPv6AddressUsing Python's
ipaddressstdlib module.2.4 Implement
from_net_addr(addr) → Multiaddrandto_net_addr(ma) → socket address2.5 Implement IP classification functions
2.6 Implement well-known constants
2.7 Implement
interface_multiaddrs() → list[Multiaddr]Using
psutil(already a dependency) orsocket.if_nameindex().2.8 Implement Dial/Listen wrappers (optional, lower priority)
Using Python's
asyncioortriofor async networking.Phase 3 – IP Classification & Filtering
Goal: Add address filtering (Accept/Deny) and enhance IP classification.
Estimated effort: 1–2 weeks
3.1 Implement
Filtersclass3.2 Add collection utilities
3.3 Add
multiaddr_to_ipnet(ma)3.4 Add
resolve_unspecified_addresses()Phase 4 – Pattern Matching, CLI & Polish
Goal: Add advanced features and tooling.
Estimated effort: 2–3 weeks
4.1 Implement pattern matching (
megequivalent)Create a
multiaddr.patternsmodule with regex-like matching for multiaddr components:This is lower priority since it's in Go's experimental
x/megpackage.4.2 Implement CLI tool
Register as console script:
4.3 Add
ForEachutility4.4 Add
addr_match(match, addrs)4.5 Add benchmark suite
Using
pytest-benchmark:Appendix A – Protocol Coverage Detail
Both implementations support 38 protocols. The only difference:
plaintextv2(0x706C61)Python has an additional
P_P2P_WEBSOCKET_STARconstant defined but Go also supports it viap2p-websocket-star.Both implementations support the
ipfs→p2palias.Appendix B – Python-Exclusive Features
These features exist in Python but not in Go:
Multiaddrimplementscollections.abc.Mappingwith.keys(),.values(),.items()ProtocolRegistrywith lockinglock(),copy(unlock=True),add_alias_name(),add_alias_code()/dns,/dns4,/dns6,/dnsaddrwith recursive resolution, peer ID preservation, and cancellationdecapsulate_code(code)get_peer_id()resolve()async methodget_thin_waist_addresses()psutilget_multiaddr_options()StringParseError,BinaryParseError,ProtocolLookupError,ProtocolExistsError,ProtocolRegistryLocked,ResolutionError,RecursionLimitErrorsplit(maxsplit)parameterSummary Timeline
Componenttype,Compare(), JSON marshaling,SplitFirst/SplitLast/SplitFunc,plaintextv2multiaddr.netpackage:dial_args,from_ip/to_ip,from_net_addr/to_net_addr, IP classification,interface_multiaddrs()Filtersclass,filter_addrs,contains,unique,multiaddr_to_ipnet, private/public CIDR listsmultiaddr.patterns, CLI tool,ForEach,addr_match, benchmarksTotal estimated effort: 8–12 weeks
The highest-impact gap is the network layer (Phase 2) — Go's
manetpackage is used extensively throughout the libp2p ecosystem for Dial/Listen operations and IP classification. TheComponenttype (Phase 1) is the second priority since it enables efficient component-level manipulation that Go users take for granted.Python's DNS resolver and async resolution are genuine advantages that should be preserved and documented as differentiators.
All reactions