swarm: move AddCertHashes to swarm (#3330)

This commit is contained in:
sukun
2025-08-07 22:41:24 +05:30
committed by GitHub
parent 02e583d319
commit fc4a618881
5 changed files with 111 additions and 60 deletions

View File

@@ -12,11 +12,8 @@ import (
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/transport"
"github.com/libp2p/go-libp2p/p2p/host/basic/internal/backoff"
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
libp2pwebrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc"
libp2pwebtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
"github.com/libp2p/go-netroute"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
@@ -44,7 +41,7 @@ type addrsManager struct {
natManager NATManager
addrsFactory AddrsFactory
listenAddrs func() []ma.Multiaddr
transportForListening func(ma.Multiaddr) transport.Transport
addCertHashes func([]ma.Multiaddr) []ma.Multiaddr
observedAddrsManager observedAddrsManager
interfaceAddrs *interfaceAddrsCache
addrsReachabilityTracker *addrsReachabilityTracker
@@ -74,7 +71,7 @@ func newAddrsManager(
natmgr NATManager,
addrsFactory AddrsFactory,
listenAddrs func() []ma.Multiaddr,
transportForListening func(ma.Multiaddr) transport.Transport,
addCertHashes func([]ma.Multiaddr) []ma.Multiaddr,
observedAddrsManager observedAddrsManager,
addrsUpdatedChan chan struct{},
client autonatv2Client,
@@ -85,7 +82,7 @@ func newAddrsManager(
as := &addrsManager{
bus: bus,
listenAddrs: listenAddrs,
transportForListening: transportForListening,
addCertHashes: addCertHashes,
observedAddrsManager: observedAddrsManager,
natManager: natmgr,
addrsFactory: addrsFactory,
@@ -514,51 +511,6 @@ func (a *addrsManager) appendObservedAddrs(dst []ma.Multiaddr, listenAddr ma.Mul
return dst
}
func (a *addrsManager) addCertHashes(addrs []ma.Multiaddr) []ma.Multiaddr {
if a.transportForListening == nil {
return addrs
}
// TODO(sukunrt): Move this to swarm.
// There are two parts to determining our external address
// 1. From the NAT device, or identify, or other such STUN like mechanism.
// All that matters here is (internal_ip, internal_port, tcp) => (external_ip, external_port, tcp)
// The rest of the address should be cut and appended to the external one.
// 2. The user provides us with the address (/ip4/1.2.3.4/udp/1/webrtc-direct) and we add the certhash.
// This API should be where the transports are, i.e. swarm.
//
// It would have been nice to remove this completely and just work with
// mapping the interface thinwaist addresses (tcp, 192.168.18.18:4000 => 1.2.3.4:4577)
// but that is only convenient if we're using the same port for listening on
// all transports which share the same thinwaist protocol. If you listen
// on 4001 for tcp, and 4002 for websocket, then it's a terrible API.
type addCertHasher interface {
AddCertHashes(m ma.Multiaddr) (ma.Multiaddr, bool)
}
for i, addr := range addrs {
wtOK, wtN := libp2pwebtransport.IsWebtransportMultiaddr(addr)
webrtcOK, webrtcN := libp2pwebrtc.IsWebRTCDirectMultiaddr(addr)
if (wtOK && wtN == 0) || (webrtcOK && webrtcN == 0) {
t := a.transportForListening(addr)
if t == nil {
continue
}
tpt, ok := t.(addCertHasher)
if !ok {
continue
}
addrWithCerthash, added := tpt.AddCertHashes(addr)
if !added {
log.Warnf("Couldn't add certhashes to multiaddr: %s", addr)
continue
}
addrs[i] = addrWithCerthash
}
}
return addrs
}
func areAddrsDifferent(prev, current []ma.Multiaddr) bool {
// TODO: make the sorted nature of ma.Unique a guarantee in multiaddrs
prev = ma.Unique(prev)

View File

@@ -177,6 +177,7 @@ type addrsManagerArgs struct {
AddrsFactory AddrsFactory
ObservedAddrsManager observedAddrsManager
ListenAddrs func() []ma.Multiaddr
AddCertHashes func([]ma.Multiaddr) []ma.Multiaddr
AutoNATClient autonatv2Client
Bus event.Bus
}
@@ -196,8 +197,15 @@ func newAddrsManagerTestCase(t *testing.T, args addrsManagerArgs) addrsManagerTe
args.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr { return addrs }
}
addrsUpdatedChan := make(chan struct{}, 1)
addCertHashes := func(addrs []ma.Multiaddr) []ma.Multiaddr {
return addrs
}
if args.AddCertHashes != nil {
addCertHashes = args.AddCertHashes
}
am, err := newAddrsManager(
eb, args.NATManager, args.AddrsFactory, args.ListenAddrs, nil, args.ObservedAddrsManager, addrsUpdatedChan, args.AutoNATClient, true, prometheus.DefaultRegisterer,
eb, args.NATManager, args.AddrsFactory, args.ListenAddrs, addCertHashes, args.ObservedAddrsManager, addrsUpdatedChan, args.AutoNATClient, true, prometheus.DefaultRegisterer,
)
require.NoError(t, err)

View File

@@ -18,7 +18,6 @@ import (
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/record"
"github.com/libp2p/go-libp2p/core/transport"
"github.com/libp2p/go-libp2p/p2p/host/autonat"
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
"github.com/libp2p/go-libp2p/p2p/host/pstoremanager"
@@ -232,12 +231,6 @@ func NewHost(n network.Network, opts *HostOpts) (*BasicHost, error) {
if opts.NATManager != nil {
natmgr = opts.NATManager(h.Network())
}
var tfl func(ma.Multiaddr) transport.Transport
if s, ok := h.Network().(interface {
TransportForListening(ma.Multiaddr) transport.Transport
}); ok {
tfl = s.TransportForListening
}
if opts.AutoNATv2 != nil {
h.autonatv2 = opts.AutoNATv2
@@ -247,12 +240,23 @@ func NewHost(n network.Network, opts *HostOpts) (*BasicHost, error) {
if h.autonatv2 != nil {
autonatv2Client = h.autonatv2
}
// Create addCertHashes function with interface assertion for swarm
addCertHashesFunc := func(addrs []ma.Multiaddr) []ma.Multiaddr {
return addrs
}
if swarm, ok := h.Network().(interface {
AddCertHashes(addrs []ma.Multiaddr) []ma.Multiaddr
}); ok {
addCertHashesFunc = swarm.AddCertHashes
}
h.addressManager, err = newAddrsManager(
h.eventbus,
natmgr,
addrFactory,
h.Network().ListenAddresses,
tfl,
addCertHashesFunc,
h.ids,
h.addrsUpdatedChan,
autonatv2Client,

View File

@@ -961,3 +961,31 @@ func (r ResolverFromMaDNS) ResolveDNSComponent(ctx context.Context, maddr ma.Mul
}
return addrs, nil
}
// AddCertHashes adds certificate hashes to relevant transport addresses, if there
// are no certhashes already present on the method. It mutates `listenAddrs`.
// This method is useful for adding certhashes to public addresses discovered
// via identify, nat mapping, or provided by the user.
func (s *Swarm) AddCertHashes(listenAddrs []ma.Multiaddr) []ma.Multiaddr {
type addCertHasher interface {
AddCertHashes(m ma.Multiaddr) (ma.Multiaddr, bool)
}
for i, addr := range listenAddrs {
t := s.TransportForListening(addr)
if t == nil {
continue
}
tpt, ok := t.(addCertHasher)
if !ok {
continue
}
addrWithCerthash, added := tpt.AddCertHashes(addr)
if !added {
log.Warnf("Couldn't add certhashes to multiaddr: %s", addr)
continue
}
listenAddrs[i] = addrWithCerthash
}
return listenAddrs
}

View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"slices"
"strings"
"sync"
"testing"
@@ -567,3 +568,61 @@ func TestListenCloseCount(t *testing.T) {
_, err := remainingAddrs[0].ValueForProtocol(ma.P_TCP)
require.NoError(t, err, "expected the TCP address to still be present")
}
func TestAddCertHashes(t *testing.T) {
s := GenSwarm(t)
listenAddrs := s.ListenAddresses()
splitCertHashes := func(a ma.Multiaddr) (prefix, certhashes ma.Multiaddr, ok bool) {
for i, c := range a {
if c.Protocol().Code == ma.P_CERTHASH {
return prefix, a[i:], true
}
prefix = append(prefix, c)
}
return prefix, certhashes, false
}
addrWithNewIPPort := func(addr ma.Multiaddr, newIPPort ma.Multiaddr) ma.Multiaddr {
a := slices.Clone(addr)
a[0] = newIPPort[0]
a[1] = newIPPort[1]
return a
}
publicIPPort := []ma.Multiaddr{
ma.StringCast("/ip4/1.1.1.1/udp/1"),
ma.StringCast("/ip4/1.2.3.4/udp/1"),
ma.StringCast("/ip6/2005::/udp/1"),
}
certHashComponent := ma.StringCast("/certhash/uEgNmb28")
for _, a := range listenAddrs {
prefix, certhashes, ok := splitCertHashes(a)
if !ok {
continue
}
var publicAddrs []ma.Multiaddr
for _, tc := range publicIPPort {
publicAddrs = append(publicAddrs, addrWithNewIPPort(prefix, tc))
}
finalAddrs := s.AddCertHashes(publicAddrs)
for _, a := range finalAddrs {
_, certhash2, ok := splitCertHashes(a)
require.True(t, ok)
require.Equal(t, certhashes, certhash2)
}
// if the addr has a certhash already, check it isn't modified
publicAddrs = nil
for _, tc := range publicIPPort {
a := addrWithNewIPPort(prefix, tc)
a = append(a, certHashComponent...)
publicAddrs = append(publicAddrs, a)
}
finalAddrs = s.AddCertHashes(publicAddrs)
for _, a := range finalAddrs {
_, certhash2, ok := splitCertHashes(a)
require.True(t, ok)
require.Equal(t, certHashComponent, certhash2)
}
}
}