libp2p / go-libp2p-examples

Example libp2p applications
MIT License
339 stars 145 forks source link

questions about chat-with-rendezvous #42

Open lzl124631x opened 5 years ago

lzl124631x commented 5 years ago

Background

I started 4 chat-with-rendezvous locally with port 3000, 4000, 5000, 6000 respectively.

./chat -listen /ip4/127.0.0.1/tcp/3000

My forked example repo: https://github.com/lzl124631x/go-libp2p-examples/commit/adb0eb38bef472749742ada537c431490c019005

Question 1

Sometimes I saw log like this:

20:20:39.958  INFO rendezvous: Connected to: {<peer.ID Qm*UB48gJ> []} chat.go:183

while other times I saw:

20:20:59.372  INFO rendezvous: Connected to: {<peer.ID Qm*UB48gJ> [/p2p-circuit /ip4/127.0.0.1/tcp/3000 /ip4/73.225.14.166/tcp/3000]} chat.go:183

So sometimes I can see the multiaddr but sometimes not. Why?

Question 2

I saw message like this:

// For node 5000
20:33:51.088  INFO rendezvous: Host created. I'm  /ip4/127.0.0.1/tcp/5000/ipfs/Qmf4Rh2JY5DfPMrR2YjKUsLhAoeG6X4cKfTxiR1NrVaHW3 chat.go:107
20:33:57.021  INFO rendezvous: Got a new stream! local /ip4/127.0.0.1/tcp/5000 remote /ip4/127.0.0.1/tcp/59919 chat.go:25
// For node 6000
20:33:57.021  INFO rendezvous: Connected to: local [/p2p-circuit /ip4/127.0.0.1/tcp/6000] remote {<peer.ID Qm*rVaHW3> []} chat.go:183
20:35:54.115  INFO rendezvous: Got a new stream! local /ip4/127.0.0.1/tcp/59919 remote /ip4/127.0.0.1/tcp/5000 chat.go:25

Why was there a host with port 59919 created for 5000?

@Stebalien @upperwal @vyzo

vyzo commented 5 years ago

So sometimes I can see the multiaddr but sometimes not. Why?

not sure about that.

Why was there a host with port 59919 created for 5000?

that's an ephemeral port, it is created by the kernel when you make an outbound connection.

anacrolix commented 5 years ago

@lzl124631x What OS?

lzl124631x commented 5 years ago

@anacrolix

Richards-MacBook-Pro:~ ricl$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.1
BuildVersion:   18B75
anacrolix commented 5 years ago

I've forgotten why I asked that, but it was relevant at the time.

upperwal commented 5 years ago

Hey @lzl124631x. With reference to your first question:

Task:

  1. Set yourself as a provider to id "key".
  2. Find other peers with id "key".

Note: Locally known (cached) providers are stored in something called a providerSet. A call to FindProviders, FindProvidersAsync or Provide will be responsible for updating any new peer to the providerSet. (there might be other functions which can update providerSet)

The situation you are facing arises when your node, say X tries to connect to some other node, say Y which is already connected to the bootstrap node, B (or any node common to X and Y).

  1. To begin with, Y wants to connect to B. After successfully connecting, B stores Y in its closest peers list and vice versa. Y also called Y.dht.Provide("key") to be a provider for "key".
  2. Now when X connects to B, they connect successfully.
  3. When X.dht.Provide() is called: 3.1 X adds itself to the providerSet but with empty address i.e. []. (This is important) 3.2 It then calls X.dht.GetClosestPeers which fetches second degree peers from the NearestPeers i.e. B for now. 3.3 B will return Y along with its addresses. Now, X will connect to Y. [I think this step was added to improve your connections automatically. A node will automatically connect to its second degree nodes if you have insufficient number of connections.] 3.4 At this point node X and Y are connected to each other. Although Y is not added in the providerSet of X and vice versa.
  4. When X.dht.FindProvider("key") is called: 4.1 X will run a query to find if NearestPeers can be a provider for "key". 4.2 NearestPeers to X at this time are B and Y and both will return a list of peers they know can be a provider for "key". 4.3 Y will return itself as it can be a provider for "key" but it will return empty list of address for itself. B also returns Y as B knows that Y can be a provider but because B knows Y's addresses, B returns a full list of addresses of Y. 4.4 Now the problem is which query returns first. If the answer from Y is returned first it will be an empty list of address and if B returns you will get a full list of addresses. Although you can overwrite the empty list but PeerSet.TryAdd used here does not add the peer to the provider set if its already in the set even if the addresses are empty.

When FindProvider returns an empty list, under above circumstances, you are already connected to that peer so host.Connect will be successful. Hence, Connected to: {<peer.ID Qm*UB48gJ> []} chat.go:183.

I think this can be reported as a bug. Ideally, a node should replace an empty list of addresses with addresses known from some other peer but findProvidersAsyncRoutine uses a chan pstore.PeerInfo for output and a result is immediately written to this chan. This means whichever query returns the result first will be written to the chan and subsequent results will be ignored.