Skip to content

Instantly share code, notes, and snippets.

@t-bast
Created July 9, 2021 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-bast/040ea733ebae4dafb798aeb888372ad0 to your computer and use it in GitHub Desktop.
Save t-bast/040ea733ebae4dafb798aeb888372ad0 to your computer and use it in GitHub Desktop.
Asymmetrical feature example

Asymmetrical features

Let's explore the following scenario. There is an asymmetrical feature F, with a clear client side and provider side. The client side of the protocol is activated by feature X. The provider side of the protocol is activated by feature Y.

Some nodes want to be a client, but not a provider, and want to connect only to nodes that are providers of this feature. They obviously turn on feature X. Since they are only clients of the feature, they cannot turn on feature Y, so how do they require their peers to support feature Y?

We can't rely on setting X mandatory, because that would only work if providers activate X. But providers may want to only be providers of the feature, and not clients of it.

AFAICT, this is not possible to express today with feature bits.

@rustyrussell
Copy link

Feature 60/61: option_needs_service_f
("client side feature")

That technically meets your needs. If a node sets bit 60, then a peer who doesn't understand it, or support it, will close the connection after init.

Now, that doesn't help our peer find servers, short of trying to connect to everyone and see who doesn't hang up! It also means, if it sets bit 61, it won't know whether it will get service_f or not.

So we can add another bit, ootion_provides_service_f 62/63.

Note why this works: the spec is asymmetric, because it only specifies the receiving side:

upon receiving unknown even feature bits that are non-zero:

- MUST fail the connection.

@t-bast
Copy link
Author

t-bast commented Jul 12, 2021

That's an interesting way to twist it, you're right, with a small clarification on the spec it could work.

Right now the way we've interpreted the spec is that after init, each peer compares the two features arrays, and if there is a mandatory bit for which the corresponding feature isn't set in the other features array, they fail the connection. That's probably what limited my thinking here, but for these new features we don't need to restrict ourselves to past behavior!

The "trick" is in the term unknown even feature bits (instead of activated even feature bits), that means we can implement that behavior with pairs of features.

Let's summarize how that would work with your proposal:

  • Case 1: client only wants to connect to peers that provide the service:
    • The client sets option_needs_service_f (mandatory)
    • The provider sets option_provides_service_f (optional)
    • The client peer can find providers via their node_announcement
    • The provider peers see in node_announcement that the client peers require this feature and only connect to them if they provide it
    • When connecting, the client peer verifies that option_provides_service_f is activated
  • Case 2: client optionally wants that service:
    • The client sets option_needs_service_f (optional)
    • Their peers are free to set option_provides_service_f or not
    • The client uses the feature protocol only when its peer has set option_provides_service_f
  • Case 3: a node wants to be both a client and provider of that service:
    • It activates option_provides_service_f (optional) and option_needs_service_f (optional or mandatory depending on whether it requires its peers to provide the service)

@rustyrussell
Copy link

That's an interesting way to twist it, you're right, with a small clarification on the spec it could work.

Right now the way we've interpreted the spec is that after init, each peer compares the two features arrays, and if there is a mandatory bit for which the corresponding feature isn't set in the other features array, they fail the connection. That's probably what limited my thinking here, but for these new features we don't need to restrict ourselves to past behavior!

Yes, this is my fault not spelling this out. Seems everyone read that into the spec (I've seen LND do the same).

I didn't want to make it more complicated as it wasn't necessary. People had trouble understanding feature bits early on at all.

The "trick" is in the term unknown even feature bits (instead of activated even feature bits), that means we can implement that behavior with pairs of features.

Let's summarize how that would work with your proposal:

* Case 1: client only wants to connect to peers that provide the service:
  
  * The client sets `option_needs_service_f` (`mandatory`)
  * The provider sets `option_provides_service_f` (`optional`)
  * The client peer can find providers via their `node_announcement`
  * The provider peers see in `node_announcement` that the client peers require this feature and only connect to them if they provide it
  * When connecting, the client peer verifies that `option_provides_service_f` is activated

No. They do nothing. The other side will disconnect.

However, they would be within their rights to require this feature, but it's not strictly necessary.

* Case 2: client optionally wants that service:
  
  * The client sets `option_needs_service_f` (`optional`)
  * Their peers are free to set `option_provides_service_f` or not
  * The client uses the feature protocol only when its peer has set `option_provides_service_f`

Indeed.

* Case 3: a node wants to be both a client and provider of that service:
  
  * It activates  `option_provides_service_f` (`optional`) and `option_needs_service_f` (`optional` or `mandatory` depending on whether it requires its peers to provide the service)

Yep, makes sense.

@t-bast
Copy link
Author

t-bast commented Aug 9, 2021

Great, thanks for the clarifications, this now makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment