I've been trying to figure out how to integrate the signed routing records (or maybe signed address records? names are hard) into the DHT and other peer routing systems in go-libp2p. This issue is just a place to think out loud about it and get feedback from other people before I start diving in too deep.
In my opinion so far, it looks like the simplest way to go would be to add another field (containing the signed record) to the peer.AddrInfo struct, since it's used in all the places where we'd like to add signed addresses - mostly the PeerRouting and ContentRouting interfaces, but also in the kad-dht internals.
Extending the AddrInfo
struct with a new field would mean that we wouldn't need to define new SignedPeerRouting
and SignedContentRouting
interfaces; we could just use the existing interfaces and have the implementations include the signed address records if they were able to find them, and leave the field nil
if they can't. Callers who care about signed addresses could use the new field,
and callers who don't could ignore it.
This seems like it would make updating the DHT much simpler than defining new interfaces that are essentially copies of PeerRouting
and ContentRouting
but with a different return type. For example, this gnarly findProvidersAsync implementation could just be tweaked to include signed records in the results, rather than having to duplicate the whole method to return signed records instead of AddrInfo
s.
However, it turns out that adding a field to a struct is a breaking change, since people might be embedding your struct in their own types and you can't guarantee that the new field won't collide with one of theirs. Since we're making other breaking changes to the Peerstore
interface (in #73) to add signed records in the first place, maybe this isn't a huge deal if we communicate it clearly.
The bigger question is whether we really do want separate interfaces for finding signed peer addresses for design / architecture reasons. Maybe conflating the two is a bad idea, and they are different enough to warrant separate interfaces. In my mind they seem really related - when I call FindPeer
, I just want to know the best way to connect to the target peer, and it seems like that should include the signed addresses if possible.
One argument for making them separate is that I might only want signed addresses, and I don't want calls to FindProvidersAsync
to give me a bunch of useless unsigned results.
Another reason is that peer.AddrInfo
seems to be used in a lot of places, and shoving a field in there because it's convenient for this one use case might be rude 😄
So, if we're not extending peer.AddrInfo
, it seems like we want to define something like this:
// SignedPeerRouting is for finding SignedRoutingState records for remote
// peers. SignedRoutingState records contain address information that has been
// issued by the peer itself and signed to authenticate and prevent tampering.
// SignedPeerRouting may be used instead of or in addition to PeerRouting,
// depending on the security requirements of the system.
type SignedPeerRouting interface {
// FindPeerRoutingState searches for a peer with the given ID and
// returns a SignedRoutingState record produced and signed by that peer.
FindPeerRoutingState(context.Context, peer.ID) (*SignedRoutingState, error)
}
// SignedContentRouting is similar to ContentRouting, in that it finds information
// about who has what content. However, addresses for content providers are returned
// in the form of SignedRoutingState records, which are issued and signed by the
// providing peer themselves.
type SignedContentRouting interface {
// Search for peers who are able to provide a given key. A SignedRoutingState
// record will be sent over the returned channel for each discovered content provider.
FindSignedProvidersAsync(context.Context, cid.Cid, int) <-chan SignedRoutingState
}
Where SignedRoutingState
is the address record defined in #73, which contains the original signed envelope along with an "unpacked" address list.
Anyway, that's where my head is at. What do people think?