Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first hack at a nice libp2p constructor #241

Merged
merged 6 commits into from
Jan 7, 2018
Merged

Conversation

whyrusleeping
Copy link
Contributor

Trying to make using go-libp2p as simple as possible

@ghost ghost assigned whyrusleeping Nov 4, 2017
@ghost ghost added the status/in-progress In progress label Nov 4, 2017
Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

libp2p.go Outdated

func DefaultConfig() *Config {
// Create a multiaddress
addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be 0.0.0.0? Maybe, to just accept the address as a parameter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel very uneasy about having defaults which open your ports to the world. AWS warns about it, systemd gets shitstorms about it, and anyone building something on top should probably not use the default anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone building something on top should probably not use the default anyway.

Then, IMO, it should be called "testing" or something like that. In this case, you're setting up a service that's supposed to be accessible from the internet. Users will be very confused if it doesn't listen on a public address by default. However, IMO, using a default address probably isn't the best idea anyways as there's no reasonable default port.

libp2p.go Outdated
yamux "github.com/whyrusleeping/go-smux-yamux"
)

type Config struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be kind of nice if this (or some subset with the key etc.) could be serialized/deserialized easily.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... that would be nice. Definitely something that can be done later though, As i think it deserves a bit more thought.

libp2p.go Outdated
Reporter metrics.Reporter
}

func Construct(ctx context.Context, cfg *Config) (host.Host, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if having New(ctx) and NewWithConfig(ctx, cfg) would be nicer. At least for me as a new user it would be more straightforward to understand what those methods do anf how they work

Copy link
Contributor

@hsanjuan hsanjuan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of useful but note it introduces duality for "defaults" that will be harder to maintain. swarm.NewNetwork* provides a way of getting a default swarm. This provides a second parallel way.

// Set up stream multiplexer
tpt := msmux.NewBlankTransport()

// By default, support yamux and multiplex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me that here the supported transports are slightly different than here: https://github.com/libp2p/go-libp2p-swarm/blob/master/swarm.go

I thought that swarm.NewNetworkWithProtector would be the default way of doing things so any updates to that should happen in swarm?

At first sight, it seems that this function is just re-doing what PSTransport provides:

// PSTransport is the default peerstream transport that will be used by
// any libp2p swarms.
var PSTransport pst.Transport

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'm actually thinking the best path forward here is to remove that logic from the swarm code, and make this place be the default. The reason being that swarm is much deeper in the dependency tree, and making it depend on extra packages makes it harder to update those packages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me 👍

@whyrusleeping
Copy link
Contributor Author

I want to try and pull some wisdom from this blog post into this PR: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis

@whyrusleeping
Copy link
Contributor Author

I'm just not sure what to make the functions operate on

@dignifiedquire
Copy link
Member

I'm just not sure what to make the functions operate on

From my understanding this would be something like this

// current version in the example
basicHost, err := libp2p.NewWithCfg(ctx, &libp2p.Config{
	ListenAddrs: []ma.Multiaddr{addr},
})

// functional version 
basicHost, err := libp2p.New(ctx, libp2p.ListenAddrs(addr))

// functional config - default config
basicHost, err := libp2p.New(ctx)

with impl being something like this

func ListenAddrs(addrs ...ma.Multiaddr) func (*Config) error {
  return func (c *Config) error {
    c.ListenAddrs = addrs
  }
}

func New(ctx context.Context, options ...func(*Config) error) (host, error) {
  cfg := Config{}
  for _, option := range options {
    err := option(cfg)
    // handle err
  }
  return NewWithCfg(ctx, cfg)
}

@whyrusleeping
Copy link
Contributor Author

@dignifiedquire yeah, having things operate on the config seems like the most reasonable way forward. I'll start on that.

What do you think about still having the option to use the config directly? I'm not really sure because it adds extra things for the user to have to think about.

@dignifiedquire
Copy link
Member

What do you think about still having the option to use the config directly

Yeah I would drop that option for users to avoid confusion. We can use NewWithCfg and make it private, but having it exposed would be just confusing for users

@whyrusleeping
Copy link
Contributor Author

There, how does that look?

libp2p.go Outdated

type Option func(cfg *Config) error

func Transports(tpts []transport.Transport) Option {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function signatures are inconsistent, would be nice if they all either take an array or varargs, as currently two of them (Transports and ListenAddrs) take an array but ListenAddrStrings takes varargs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have been a little tired writing this... >.>

libp2p.go Outdated
}
}

func NoSecio(cfg *Config) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to have a Secio or WithSecio one as well, such that you can make it explicit to always use it, independent of the defaults

libp2p.go Outdated
return nil
}

func WithMuxer(m mux.Transport) Option {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are these called With<> and Transports not WithTransports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we overload the term Transports. Transports also applies to things like tcp, quic, websockets, etc

@ghost
Copy link

ghost commented Dec 15, 2017

You might like my ideas for this constructor API, but they're pretty different.
Generally I'd like to be closer js-libp2p, and "leak" fewer of swarm's/host's
details across the surface (in particular we should not hardcode secio in
the interface).

Most of the option functions can be simplified as loaders for certain kinds of
modules (as in libp2p modules): transport, muxer, encryption, private networking,
discovery. At a later point, when we have transport capabilities, many of them
can simply be collapsed into the transport module category.

  • Identity(privkey)
  • ListenAddrs(string...)
  • AnnounceAddrs(string...)
  • Transports(tpt.Transport...)
  • Muxer(msmux.Transport...)
  • Encryption(...) / NoEncryption()
  • PrivateNetworking(protector)
  • Discovery(disc.Discovery...) (the discovery interface is still todo in go-libp2p)
  • As for the naming pattern, e.g. WithFoo, FooOpt, etc., I don't have a strong opinion at the moment.

About the default config: why not make it a separate package? We can have
packages for various configurations, while go-libp2p itself depends "mostly"
on interfaces. (The muxers should keep being configurable right here.)

import (
    libp2p "github.com/libp2p/go-libp2p"
    cfg "github.com/libp2p/go-libp2p-default"
)

func main() {
  h, err := libp2p.New(context.TODO(), cfg.Config()...)
}

Also at a later point, I'd like to be able to construct a node simply by
passing a bunch of protocol identifiers, e.g.:

StrTransports("/ws/1.0.0", "/secio/1.0.0", "/mplex/6.3.0")
StrDiscovery("/mdns/1.0.0", "/udp/1.0.0")
// libp2p modules register themselves similar to how multiaddr protocols do it.

But that's really not neccessary at the moment.

@whyrusleeping
Copy link
Contributor Author

@lgierth there, updated with some of your input

@hsanjuan
Copy link
Contributor

Is this go-ish? I don't think I've seen these constructs in Go before. I find it innovative, kind of javascrip-ty.

It's fancy, I might like it, but I find the NewWithCfg initialization way more natural and straightforward in the end (and require less code). Perhaps I'm old fashioned... but let's keep that method exported in any case.

@hsanjuan
Copy link
Contributor

oh, just saw the dave cheney post, I guess it's go-ish then :)

@ghost ghost self-requested a review December 17, 2017 21:51
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:):):)

@whyrusleeping
Copy link
Contributor Author

alright, gonna merge this for now. The next step is to try out using it in go-ipfs to construct our libp2p node.

@whyrusleeping whyrusleeping merged commit 7db6e54 into master Jan 7, 2018
@ghost ghost removed the status/in-progress In progress label Jan 7, 2018
@whyrusleeping whyrusleeping deleted the feat/builder branch January 7, 2018 20:42
@Stebalien Stebalien mentioned this pull request May 11, 2021
27 tasks
marten-seemann pushed a commit that referenced this pull request Apr 21, 2022
marten-seemann added a commit that referenced this pull request Aug 17, 2022
* bump go.mod to Go 1.17 and run go fix

* update .github/workflows/automerge.yml

* update .github/workflows/go-test.yml

* update .github/workflows/go-check.yml

* remove unneeded nil check in ECDSAPublicKey.Verify

Co-authored-by: web3-bot <[email protected]>
Co-authored-by: Marten Seemann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants