// Package peerstore provides types and interfaces for local storage of address information, // metadata, and public key material about libp2p peers. package peerstore import ( "context" "errors" "io" "math" "time" ic "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/record" ma "github.com/multiformats/go-multiaddr" ) var ErrNotFound = errors.New("item not found") var ( // AddressTTL is the expiration time of addresses. AddressTTL = time.Hour // TempAddrTTL is the ttl used for a short-lived address. TempAddrTTL = time.Minute * 2 // RecentlyConnectedAddrTTL is used when we recently connected to a peer. // It means that we are reasonably certain of the peer's address. RecentlyConnectedAddrTTL = time.Minute * 30 // OwnObservedAddrTTL is used for our own external addresses observed by peers. OwnObservedAddrTTL = time.Minute * 30 ) // Permanent TTLs (distinct so we can distinguish between them, constant as they // are, in fact, permanent) const ( // PermanentAddrTTL is the ttl for a "permanent address" (e.g. bootstrap nodes). PermanentAddrTTL = math.MaxInt64 - iota // ConnectedAddrTTL is the ttl used for the addresses of a peer to whom // we're connected directly. This is basically permanent, as we will // clear them + re-add under a TempAddrTTL after disconnecting. ConnectedAddrTTL ) // Peerstore provides a thread-safe store of Peer related // information. type Peerstore interface { io.Closer AddrBook KeyBook PeerMetadata Metrics ProtoBook // PeerInfo returns a peer.PeerInfo struct for given peer.ID. // This is a small slice of the information Peerstore has on // that peer, useful to other services. PeerInfo(peer.ID) peer.AddrInfo // Peers returns all the peer IDs stored across all inner stores. Peers() peer.IDSlice } // PeerMetadata can handle values of any type. Serializing values is // up to the implementation. Dynamic type introspection may not be // supported, in which case explicitly enlisting types in the // serializer may be required. // // Refer to the docs of the underlying implementation for more // information. type PeerMetadata interface { // Get / Put is a simple registry for other peer-related key/value pairs. // If we find something we use often, it should become its own set of // methods. This is a last resort. Get(p peer.ID, key string) (interface{}, error) Put(p peer.ID, key string, val interface{}) error // RemovePeer removes all values stored for a peer. RemovePeer(peer.ID) } // AddrBook holds the multiaddrs of peers. type AddrBook interface { // AddAddr calls AddAddrs(p, []ma.Multiaddr{addr}, ttl) AddAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration) // AddAddrs gives this AddrBook addresses to use, with a given ttl // (time-to-live), after which the address is no longer valid. // If the manager has a longer TTL, the operation is a no-op for that address AddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration) // SetAddr calls mgr.SetAddrs(p, addr, ttl) SetAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration) // SetAddrs sets the ttl on addresses. This clears any TTL there previously. // This is used when we receive the best estimate of the validity of an address. SetAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration) // UpdateAddrs updates the addresses associated with the given peer that have // the given oldTTL to have the given newTTL. UpdateAddrs(p peer.ID, oldTTL time.Duration, newTTL time.Duration) // Addrs returns all known (and valid) addresses for a given peer. Addrs(p peer.ID) []ma.Multiaddr // AddrStream returns a channel that gets all addresses for a given // peer sent on it. If new addresses are added after the call is made // they will be sent along through the channel as well. AddrStream(context.Context, peer.ID) <-chan ma.Multiaddr // ClearAddresses removes all previously stored addresses. ClearAddrs(p peer.ID) // PeersWithAddrs returns all the peer IDs stored in the AddrBook. PeersWithAddrs() peer.IDSlice } // CertifiedAddrBook manages "self-certified" addresses for remote peers. // Self-certified addresses are contained in peer.PeerRecords // which are wrapped in a record.Envelope and signed by the peer // to whom they belong. // // Certified addresses (CA) are generally more secure than uncertified // addresses (UA). Consequently, CAs beat and displace UAs. When the // peerstore learns CAs for a peer, it will reject UAs for the same peer // (as long as the former haven't expired). // Furthermore, peer records act like sequenced snapshots of CAs. Therefore, // processing a peer record that's newer than the last one seen overwrites // all addresses with the incoming ones. // // This interface is most useful when combined with AddrBook. // To test whether a given AddrBook / Peerstore implementation supports // certified addresses, callers should use the GetCertifiedAddrBook helper or // type-assert on the CertifiedAddrBook interface: // // if cab, ok := aPeerstore.(CertifiedAddrBook); ok { // cab.ConsumePeerRecord(signedPeerRecord, aTTL) // } type CertifiedAddrBook interface { // ConsumePeerRecord adds addresses from a signed peer.PeerRecord (contained in // a record.Envelope), which will expire after the given TTL. // // The 'accepted' return value indicates that the record was successfully processed // and integrated into the CertifiedAddrBook state. If 'accepted' is false but no // error is returned, it means that the record was ignored, most likely because // a newer record exists for the same peer. // // Signed records added via this method will be stored without // alteration as long as the address TTLs remain valid. The Envelopes // containing the PeerRecords can be retrieved by calling GetPeerRecord(peerID). // // If the signed PeerRecord belongs to a peer that already has certified // addresses in the CertifiedAddrBook, the new addresses will replace the // older ones, if the new record has a higher sequence number than the // existing record. Attempting to add a peer record with a // sequence number that's <= an existing record for the same peer will not // result in an error, but the record will be ignored, and the 'accepted' // bool return value will be false. // // If the CertifiedAddrBook is also an AddrBook (which is most likely the case), // adding certified addresses for a peer will *replace* any // existing non-certified addresses for that peer, and only the certified // addresses will be returned from AddrBook.Addrs thereafter. // // Likewise, once certified addresses have been added for a given peer, // any non-certified addresses added via AddrBook.AddAddrs or // AddrBook.SetAddrs will be ignored. AddrBook.SetAddrs may still be used // to update the TTL of certified addresses that have previously been // added via ConsumePeerRecord. ConsumePeerRecord(s *record.Envelope, ttl time.Duration) (accepted bool, err error) // GetPeerRecord returns an Envelope containing a PeerRecord for the // given peer id, if one exists. // Returns nil if no signed PeerRecord exists for the peer. GetPeerRecord(p peer.ID) *record.Envelope } // GetCertifiedAddrBook is a helper to "upcast" an AddrBook to a // CertifiedAddrBook by using type assertion. If the given AddrBook // is also a CertifiedAddrBook, it will be returned, and the ok return // value will be true. Returns (nil, false) if the AddrBook is not a // CertifiedAddrBook. // // Note that since Peerstore embeds the AddrBook interface, you can also // call GetCertifiedAddrBook(myPeerstore). func GetCertifiedAddrBook(ab AddrBook) (cab CertifiedAddrBook, ok bool) { cab, ok = ab.(CertifiedAddrBook) return cab, ok } // KeyBook tracks the keys of Peers. type KeyBook interface { // PubKey stores the public key of a peer. PubKey(peer.ID) ic.PubKey // AddPubKey stores the public key of a peer. AddPubKey(peer.ID, ic.PubKey) error // PrivKey returns the private key of a peer, if known. Generally this might only be our own // private key, see // https://discuss.libp2p.io/t/what-is-the-purpose-of-having-map-peer-id-privatekey-in-peerstore/74. PrivKey(peer.ID) ic.PrivKey // AddPrivKey stores the private key of a peer. AddPrivKey(peer.ID, ic.PrivKey) error // PeersWithKeys returns all the peer IDs stored in the KeyBook. PeersWithKeys() peer.IDSlice // RemovePeer removes all keys associated with a peer. RemovePeer(peer.ID) } // Metrics tracks metrics across a set of peers. type Metrics interface { // RecordLatency records a new latency measurement RecordLatency(peer.ID, time.Duration) // LatencyEWMA returns an exponentially-weighted moving avg. // of all measurements of a peer's latency. LatencyEWMA(peer.ID) time.Duration // RemovePeer removes all metrics stored for a peer. RemovePeer(peer.ID) } // ProtoBook tracks the protocols supported by peers. type ProtoBook interface { GetProtocols(peer.ID) ([]protocol.ID, error) AddProtocols(peer.ID, ...protocol.ID) error SetProtocols(peer.ID, ...protocol.ID) error RemoveProtocols(peer.ID, ...protocol.ID) error // SupportsProtocols returns the set of protocols the peer supports from among the given protocols. // If the returned error is not nil, the result is indeterminate. SupportsProtocols(peer.ID, ...protocol.ID) ([]protocol.ID, error) // FirstSupportedProtocol returns the first protocol that the peer supports among the given protocols. // If the peer does not support any of the given protocols, this function will return an empty protocol.ID and a nil error. // If the returned error is not nil, the result is indeterminate. FirstSupportedProtocol(peer.ID, ...protocol.ID) (protocol.ID, error) // RemovePeer removes all protocols associated with a peer. RemovePeer(peer.ID) }