diff --git a/.travis.yml b/.travis.yml index 5163d693fc..ba87d9a80a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: language: go go: - - 1.11.x + - 1.12.x env: global: diff --git a/README.md b/README.md index dfbb9f4139..9df42c292e 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ libp2p is the product of a long, and arduous quest of understanding -- a deep di ## Usage -This repository (`go-libp2p`) serves as the entrypoint to the universe of modules that compose the Go implementation of the libp2p stack. +This repository (`go-libp2p`) serves as the entrypoint to the universe of modules that compose the Go implementation of the libp2p stack. Libp2p requires go 1.12+. -We mainly use [Go modules](https://github.com/golang/go/wiki/Modules) for our dependency and release management (and thus require go >= 1.11). In order to get the best developer experience, we recommend you do too. Otherwise, you may ocassionally encounter a breaking build as you'll be running off master (which, by definition, is not guaranteed to be stable). +We mainly use [Go modules](https://github.com/golang/go/wiki/Modules) for our dependency and release management (and thus require go >= 1.12+). In order to get the best developer experience, we recommend you do too. Otherwise, you may ocassionally encounter a breaking build as you'll be running off master (which, by definition, is not guaranteed to be stable). You can start using go-libp2p in your Go application simply by adding imports from our repos, e.g.: diff --git a/config/config.go b/config/config.go index 3bb51bf1f7..782ac10c74 100644 --- a/config/config.go +++ b/config/config.go @@ -44,6 +44,12 @@ type RoutingC func(host.Host) (routing.PeerRouting, error) // This is *not* a stable interface. Use the options defined in the root // package. type Config struct { + // UserAgent is the identifier this node will send to other peers when + // identifying itself, e.g. via the identify protocol. + // + // Set it via the UserAgent option function. + UserAgent string + PeerKey crypto.PrivKey Transports []TptC @@ -120,6 +126,7 @@ func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) { AddrsFactory: cfg.AddrsFactory, NATManager: cfg.NATManager, EnablePing: !cfg.DisablePing, + UserAgent: cfg.UserAgent, }) if err != nil { diff --git a/options.go b/options.go index 6d310f1aa3..c5ef639ebe 100644 --- a/options.go +++ b/options.go @@ -319,3 +319,11 @@ var NoTransports = func(cfg *Config) error { cfg.Transports = []config.TptC{} return nil } + +// UserAgent sets the libp2p user-agent sent along with the identify protocol +func UserAgent(userAgent string) Option { + return func(cfg *Config) error { + cfg.UserAgent = userAgent + return nil + } +} diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index 2d965bdae1..92946c5d7c 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -100,10 +100,6 @@ type HostOpts struct { // If below 0, timeouts on streams will be deactivated. NegotiationTimeout time.Duration - // IdentifyService holds an implementation of the /ipfs/id/ protocol. - // If omitted, a new *identify.IDService will be used. - IdentifyService *identify.IDService - // AddrsFactory holds a function which can be used to override or filter the result of Addrs. // If omitted, there's no override or filtering, and the results of Addrs and AllAddrs are the same. AddrsFactory AddrsFactory @@ -121,6 +117,9 @@ type HostOpts struct { // EnablePing indicates whether to instantiate the ping service EnablePing bool + + // UserAgent sets the user-agent for the host. Defaults to ClientVersion. + UserAgent string } // NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given inet.Network. @@ -154,12 +153,12 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo h.mux = opts.MultistreamMuxer } - if opts.IdentifyService != nil { - h.ids = opts.IdentifyService - } else { - // we can't set this as a default above because it depends on the *BasicHost. - h.ids = identify.NewIDService(goprocessctx.WithProcessClosing(ctx, h.proc), h) - } + // we can't set this as a default above because it depends on the *BasicHost. + h.ids = identify.NewIDService( + goprocessctx.WithProcessClosing(ctx, h.proc), + h, + identify.UserAgent(opts.UserAgent), + ) if uint64(opts.NegotiationTimeout) != 0 { h.negtimeout = opts.NegotiationTimeout diff --git a/p2p/protocol/identify/id.go b/p2p/protocol/identify/id.go index 1fa2bcf1f6..35e2e57ad8 100644 --- a/p2p/protocol/identify/id.go +++ b/p2p/protocol/identify/id.go @@ -2,6 +2,8 @@ package identify import ( "context" + "fmt" + "runtime/debug" "sync" "time" @@ -31,9 +33,27 @@ const ID = "/ipfs/id/1.0.0" // LibP2PVersion holds the current protocol version for a client running this code // TODO(jbenet): fix the versioning mess. +// XXX: Don't change this till 2020. You'll break all go-ipfs versions prior to +// 0.4.17 which asserted an exact version match. const LibP2PVersion = "ipfs/0.1.0" -var ClientVersion = "go-libp2p/3.3.4" +// ClientVersion is the default user agent. +// +// Deprecated: Set this with the UserAgent option. +var ClientVersion = "github.com/libp2p/go-libp2p" + +func init() { + bi, ok := debug.ReadBuildInfo() + if !ok { + return + } + version := bi.Main.Version + if version == "(devel)" { + ClientVersion = bi.Main.Path + } else { + ClientVersion = fmt.Sprintf("%s@%s", bi.Main.Path, bi.Main.Version) + } +} // transientTTL is a short ttl for invalidated previously connected addrs const transientTTL = 10 * time.Second @@ -47,7 +67,8 @@ const transientTTL = 10 * time.Second // * Our IPFS Agent Version // * Our public Listen Addresses type IDService struct { - Host host.Host + Host host.Host + UserAgent string ctx context.Context @@ -70,9 +91,21 @@ type IDService struct { // NewIDService constructs a new *IDService and activates it by // attaching its stream handler to the given host.Host. -func NewIDService(ctx context.Context, h host.Host) *IDService { +func NewIDService(ctx context.Context, h host.Host, opts ...Option) *IDService { + var cfg config + for _, opt := range opts { + opt(&cfg) + } + + userAgent := ClientVersion + if cfg.userAgent != "" { + userAgent = cfg.userAgent + } + s := &IDService{ - Host: h, + Host: h, + UserAgent: userAgent, + ctx: ctx, currid: make(map[network.Conn]chan struct{}), observedAddrs: NewObservedAddrSet(ctx), @@ -306,7 +339,7 @@ func (ids *IDService) populateMessage(mes *pb.Identify, c network.Conn) { // set protocol versions pv := LibP2PVersion - av := ClientVersion + av := ids.UserAgent mes.ProtocolVersion = &pv mes.AgentVersion = &av } diff --git a/p2p/protocol/identify/id_test.go b/p2p/protocol/identify/id_test.go index dedf9a094e..5c422a9092 100644 --- a/p2p/protocol/identify/id_test.go +++ b/p2p/protocol/identify/id_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/libp2p/go-eventbus" + libp2p "github.com/libp2p/go-libp2p" ic "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/event" "github.com/libp2p/go-libp2p-core/helpers" @@ -367,3 +368,40 @@ func TestIdentifyDeltaWhileIdentifyingConn(t *testing.T) { t.Fatalf("timed out while waiting for an event for the protocol changes in h2") } } + +func TestUserAgent(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + h1, err := libp2p.New( + ctx, + libp2p.UserAgent("foo"), + libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), + ) + if err != nil { + t.Fatal(err) + } + defer h1.Close() + + h2, err := libp2p.New( + ctx, + libp2p.UserAgent("bar"), + libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), + ) + if err != nil { + t.Fatal(err) + } + defer h2.Close() + + err = h1.Connect(ctx, peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) + if err != nil { + t.Fatal(err) + } + av, err := h1.Peerstore().Get(h2.ID(), "AgentVersion") + if err != nil { + t.Fatal(err) + } + if ver, ok := av.(string); !ok || ver != "bar" { + t.Errorf("expected agent version %q, got %q", "bar", av) + } +} diff --git a/p2p/protocol/identify/opts.go b/p2p/protocol/identify/opts.go new file mode 100644 index 0000000000..670b213b71 --- /dev/null +++ b/p2p/protocol/identify/opts.go @@ -0,0 +1,15 @@ +package identify + +type config struct { + userAgent string +} + +// Option is an option function for identify. +type Option func(*config) + +// UserAgent sets the user agent this node will identify itself with to peers. +func UserAgent(ua string) Option { + return func(cfg *config) { + cfg.userAgent = ua + } +}