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

Generate local alias before spawning channel #2316

Closed
wants to merge 4 commits into from
Closed

Conversation

pm47
Copy link
Member

@pm47 pm47 commented Jun 15, 2022

As a first step before addressing #2224 (comment), we need to make the alias generation asynchronous.

Peer generates the alias along with the shutdown script, and before spawning the channel.

This approach has several drawbacks:

  • changes all data classes in the early channel states
  • forces us to have a local alias in state WAIT_FOR_FUNDING_CONFIRMED, which is persisted and requires a migration. We can't set it to the real scid because we don't have it yet
  • not consistent with a future (hypothetical) multi-alias support

@pm47 pm47 requested a review from t-bast June 15, 2022 13:54
@pm47 pm47 force-pushed the local-alias-gen branch 2 times, most recently from 6fc252f to 3becfb8 Compare June 16, 2022 11:16
@t-bast
Copy link
Member

t-bast commented Jun 16, 2022

I have an alternative design for alias generation: what about inserting a new temporary channel state to generate it? Whenever we currently call acceptFundingTx, we would go to a WAIT_FOR_LOCAL_ALIAS state where we store the next state and next state data, generate the local alias (probably by sending a message to the Router), and once we receive the alias we go to the next state with the next state data. In that state we store incoming lightning messages and replay them once we have our local alias and transition to the next state. It's very simple for transient states, the only case where it may be a bit painful is the unrequested 0-conf where we are in WAIT_FOR_FUNDING_CONFIRMED (which isn't transient), this would need some prototyping...

@t-bast
Copy link
Member

t-bast commented Jun 16, 2022

Something else that comes to mind: for public channels that aren't using 0-conf (which is going to be the most common flow for most node operators), we probably shouldn't really generate a localAlias, we can probably use 0? I'm not sure if we can use that to simplify the flow or not...

@pm47
Copy link
Member Author

pm47 commented Jun 16, 2022

I have an alternative design for alias generation: what about inserting a new temporary channel state to generate it?

I thought about that, but I'd don't like adding new states because we then need to handle all the other events (remote errors, funding spent, disconnection, etc.) and it quickly becomes bulky.

Something else that comes to mind: for public channels that aren't using 0-conf (which is going to be the most common flow for most node operators), we probably shouldn't really generate a localAlias, we can probably use 0?

That's a good point, but I think we don't want to have duplicate aliases, it should be an Option in that case. It could complicate things though, let me think about it...

@t-bast
Copy link
Member

t-bast commented Jun 17, 2022

Yes, it's true, after thinking about it more I think we should keep the localAlias non-optional, it really makes the code much cleaner. In that case it makes sense to generate it right at the beginning of the channel opening flow. It can be in the Peer for now, I think that once #2247 is integrated which refactor the channel init state, I may move that in the channel's init state instead, we'll see.

@pm47 pm47 marked this pull request as ready for review June 17, 2022 07:47
eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala Outdated Show resolved Hide resolved
* .as[MyClass]
* }}}
*/
object Mutators {
Copy link
Member

Choose a reason for hiding this comment

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

Should we put that in a versioned namespace (this one would go in the version3 package since we introduced the need for it in version3 and won't need it for future versions)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe, although we sort of adopted the opposite approach for ChannelTypes0.

Copy link
Member

Choose a reason for hiding this comment

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

On the contrary, we put ChannelTypes0 in the version1 package? But it doesn't matter right now, we can move those mutators to a versioned package when we introduce new ones that are for a different codecs version.

pm47 added 3 commits June 17, 2022 11:03
Using the first 7B increases the risk of collision at migration due to
how `channel_id` is built. Collisions would happen when multiple
channels are funded with the same transaction.
Comment on lines +392 to +393
val localAlias_p = Promise[Alias]()
context.system.eventStream.publish(Register.GenerateLocalAlias(localAlias_p))
Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't very academic, but:

  • using the eventStream saves us from having to pass a reference to register all the way to the peer
  • using a Promise saves us from creating an ad-hoc actor (pipeTo doesn't work with the eventStream approach)

Copy link
Member

@t-bast t-bast left a comment

Choose a reason for hiding this comment

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

Looks good, only a few comments (and a rebase is needed).

Comment on lines +67 to +68
// at block height 1000 LN didn't exist, so all real scids less than that will never be used
val upperBound = RealShortChannelId.apply(BlockHeight(1000),1,0).toLong
Copy link
Member

Choose a reason for hiding this comment

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

Why do we use such a small range? We can go up to at least block 350 000, can't we?

Copy link
Member Author

Choose a reason for hiding this comment

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

The space is already huge : 1_099_511_627_841_536 (*) and we are checking for duplicates. I figure leaving some values available could prove handy later?

(*) So huge in fact that I believe our previous computation was invalid. If we indeed go up to block height 350 000 it gives us a ceiling of 384_829_069_721_665_536 which is about 2^59. The probability of a collision for 250 000 channels would be 0.000011 %, not 0.8 % ?

def generateLocalAlias(): Alias = {
// at block height 1000 LN didn't exist, so all real scids less than that will never be used
val upperBound = RealShortChannelId.apply(BlockHeight(1000),1,0).toLong
Alias(Random.nextLong(upperBound))
Copy link
Member

Choose a reason for hiding this comment

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

We should use the randomLong from package.scala here.

* .as[MyClass]
* }}}
*/
object Mutators {
Copy link
Member

Choose a reason for hiding this comment

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

On the contrary, we put ChannelTypes0 in the version1 package? But it doesn't matter right now, we can move those mutators to a versioned package when we introduce new ones that are for a different codecs version.

@@ -79,6 +82,13 @@ class Register extends Actor with ActorLogging {
case Some(channel) => channel.tell(msg, compatReplyTo)
case None => compatReplyTo ! ForwardShortIdFailure(fwd)
}

case GenerateLocalAlias(promise) =>
Copy link
Member

Choose a reason for hiding this comment

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

Since we re-populate the shortIds map asynchronously as channels are restored and send ShortChannelIdAssigned, we may receive that message before we've restored all channels and recovered a complete shortIds map.

I believe it won't be an issue in practice as long as the range we use for ShortChannelId.generateLocalAlias() is big enough to make collisions unlikely.

system.eventStream.subscribe(generateLocalAliasListener.ref, classOf[GenerateLocalAlias])
waitEventStreamSynced(system.eventStream)
generateLocalAliasListener.setAutoPilot {
case (_, gen:GenerateLocalAlias) =>
Copy link
Member

Choose a reason for hiding this comment

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

nit:

Suggested change
case (_, gen:GenerateLocalAlias) =>
case (_, gen: GenerateLocalAlias) =>

@pm47
Copy link
Member Author

pm47 commented Jun 29, 2022

Superseded by #2337.

@pm47 pm47 closed this Jun 29, 2022
@t-bast t-bast deleted the local-alias-gen branch August 16, 2022 09:49
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.

2 participants