-
Notifications
You must be signed in to change notification settings - Fork 269
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
Separate internal channel config from features #1852
Conversation
Did you consider modifying |
Yes I did, but I then decided to separate them. In most cases they are really unrelated and you only need one of them. The only benefit to keep them encapsulated in a |
Ok. One of my concerns was that to implement custom extensions we would need both fields, and that it may even be true for parameter validation, but overall this PR LGTM. |
IMO most custom extensions behave like channel features, so they should use only the channel type part, not the channel internal config. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've rebased #1846 on top of this (see https://github.com/ACINQ/eclair/tree/implement-upfront-shutdown-script-channel-version-migration) and everything LGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The clash between ChannelType
and ChannelTypes
is unfortunate. I would rename (maybe in a separate PR):
State
->ChannelState
Data
->ChannelData
ChannelTypes.scala
->ChannelData.scala
eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelConfigOptions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelType.scala
Outdated
Show resolved
Hide resolved
eclair-node/src/main/scala/fr/acinq/eclair/api/serde/JsonSerializers.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelType.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelType.scala
Outdated
Show resolved
Hide resolved
I agree, it's probably better to do it in a separate PR that only does that and no logic change. |
eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelConfigOptions.scala
Outdated
Show resolved
Hide resolved
0adaeaa
to
25a983e
Compare
Our current ChannelVersion field mixes two unrelated concepts: channel features (as defined in Bolt 9) and channel internals (such as custom key derivation). It is more future-proof to separate these two unrelated concepts and will make it easier to implement channel types (see lightning/bolts#880).
This will be necessary when we implement `upfront_shutdown_script`. This field can be set to `None` for all current channels as we don't support the feature yet.
25a983e
to
0b520dd
Compare
* Internal configuration option impacting the channel's structure or behavior. | ||
* This must be set when creating the channel and cannot be changed afterwards. | ||
*/ | ||
trait ChannelConfigOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
trait ChannelConfigOption { | |
trait ChannelConfig { |
or
trait ChannelConfigOption { | |
trait ChannelOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It's really a configuration option...it's not a complete configuration so ChannelConfig
feels weird IMO, and ChannelOption
is on the contrary a bit too vague, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just that everywhere we are using: channelConfig: ChannelConfigOptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's different, it's because it's a ChannelConfigOptions
, which is a collection of ChannelConfigOption
.
I agree this one could be renamed ChannelConfigOptions
-> ChannelConfig
, but not the trait on individual options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 4e0c814
|
||
object ChannelConfigOptions { | ||
|
||
def standard: ChannelConfigOptions = ChannelConfigOptions(activated = Set(FundingPubKeyBasedChannelKeyPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a val here?
def standard: ChannelConfigOptions = ChannelConfigOptions(activated = Set(FundingPubKeyBasedChannelKeyPath)) | |
val standard: ChannelConfigOptions = ChannelConfigOptions(activated = Set(FundingPubKeyBasedChannelKeyPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason, this should be a val
indeed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 4e0c814
eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version0/ChannelCodecs0.scala
Show resolved
Hide resolved
(("remoteParams" | remoteParamsCodec) :: | ||
("channelFlags" | byte) :: | ||
("localCommit" | localCommitCodec(remoteParams.fundingPubKey)) :: | ||
("remoteCommit" | remoteCommitCodec) :: | ||
("localChanges" | localChangesCodec) :: | ||
("remoteChanges" | remoteChangesCodec) :: | ||
("localNextHtlcId" | uint64overflow) :: | ||
("remoteNextHtlcId" | uint64overflow) :: | ||
("originChannels" | originsMapCodec) :: | ||
("remoteNextCommitInfo" | either(bool, waitingForRevocationCodec, publicKey)) :: | ||
("commitInput" | inputInfoCodec) :: | ||
("remotePerCommitmentSecrets" | ShaChain.shaChainCodec) :: | ||
("channelId" | bytes32) | ||
}) | ||
}).as[Commitments].decodeOnly | ||
("localCommit" | localCommitCodec) :: | ||
("remoteCommit" | remoteCommitCodec) :: | ||
("localChanges" | localChangesCodec) :: | ||
("remoteChanges" | remoteChangesCodec) :: | ||
("localNextHtlcId" | uint64overflow) :: | ||
("remoteNextHtlcId" | uint64overflow) :: | ||
("originChannels" | originsMapCodec) :: | ||
("remoteNextCommitInfo" | either(bool, waitingForRevocationCodec, publicKey)) :: | ||
("commitInput" | inputInfoCodec) :: | ||
("remotePerCommitmentSecrets" | ShaChain.shaChainCodec) :: | ||
("channelId" | bytes32)) | ||
}).as[ChannelTypes0.Commitments].decodeOnly.map[Commitments](_.migrate()).decodeOnly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The outermost parentheses can be removed (same in other file).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that when comparing both PRs to master
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 4e0c814
val channelConfigCodec: Codec[ChannelConfigOptions] = lengthDelimited(bytes).xmap(b => { | ||
val activated: Set[ChannelConfigOption] = b.bits.toIndexedSeq.reverse.zipWithIndex.collect { | ||
case (true, 0) => ChannelConfigOptions.FundingPubKeyBasedChannelKeyPath | ||
}.toSet | ||
ChannelConfigOptions(activated) | ||
}, cfg => { | ||
val indices = cfg.activated.map(_.supportBit) | ||
if (indices.isEmpty) { | ||
ByteVector.empty | ||
} else { | ||
// NB: when converting from BitVector to ByteVector, scodec pads right instead of left, so we make sure we pad to bytes *before* setting bits. | ||
var buffer = BitVector.fill(indices.max + 1)(high = false).bytes.bits | ||
indices.foreach(i => buffer = buffer.set(i)) | ||
buffer.reverse.bytes | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This codec is a bit 🤢, but unfortunately it seems scodec
doesn't work well with bitfields.
Since we use bitfields at several places, it's probably worth it to he define a generic byte-aligned, right-to-left, bitfield codec? I'll give it a try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, it's useful so that we don't unintentionally go back to storing something that isn't byte-aligned!
val indices = cfg.options.map(_.supportBit) | ||
if (indices.isEmpty) { | ||
ByteVector.empty | ||
} else { | ||
// NB: when converting from BitVector to ByteVector, scodec pads right instead of left, so we make sure we pad to bytes *before* setting bits. | ||
var buffer = BitVector.fill(indices.max + 1)(high = false).bytes.bits | ||
indices.foreach(i => buffer = buffer.set(i)) | ||
buffer.reverse.bytes | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val indices = cfg.options.map(_.supportBit) | |
if (indices.isEmpty) { | |
ByteVector.empty | |
} else { | |
// NB: when converting from BitVector to ByteVector, scodec pads right instead of left, so we make sure we pad to bytes *before* setting bits. | |
var buffer = BitVector.fill(indices.max + 1)(high = false).bytes.bits | |
indices.foreach(i => buffer = buffer.set(i)) | |
buffer.reverse.bytes | |
} | |
val indices = cfg.activated.map(_.supportBit) | |
val maxLength = indices.maxOption.getOrElse(-1) + 1 | |
val buffer = indices.foldLeft(BitVector.low(maxLength)) { | |
case (bitfield, i) => bitfield.set(i) | |
} | |
buffer.bytes.bits.reverse.bytes | |
} |
@@ -476,61 +485,11 @@ final case class RemoteParams(nodeId: PublicKey, | |||
paymentBasepoint: PublicKey, | |||
delayedPaymentBasepoint: PublicKey, | |||
htlcBasepoint: PublicKey, | |||
features: Features) | |||
features: Features, | |||
shutdownScript: Option[ByteVector]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming is inconsistent LocalParams.defaultFinalScriptPubKey
and RemoteParams.shutdownScript
Either:
LocalParams.defaultFinalScriptPubKey
andRemoteParams.upfrontFinalScriptPubKey
orLocalParams.defaultShutdownScript
andRemoteParams.upfrontShutdownScript
I would keep the upfront
in any case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's revisit the naming afterwards.
* Separate internal channel config from features Our current ChannelVersion field mixes two unrelated concepts: channel features (as defined in Bolt 9) and channel internals (such as custom key derivation). It is more future-proof to separate these two unrelated concepts and will make it easier to implement channel types (see lightning/bolts#880). * Add shutdown script to channel remote params This will be necessary when we implement `upfront_shutdown_script`. This field can be set to `None` for all current channels as we don't support the feature yet.
* Separate internal channel config from features Our current ChannelVersion field mixes two unrelated concepts: channel features (as defined in Bolt 9) and channel internals (such as custom key derivation). It is more future-proof to separate these two unrelated concepts and will make it easier to implement channel types (see lightning/bolts#880). * Add shutdown script to channel remote params This will be necessary when we implement `upfront_shutdown_script`. This field can be set to `None` for all current channels as we don't support the feature yet.
Our current ChannelVersion field mixes two unrelated concepts: channel features (as defined in Bolt 9) and channel internals (such as custom key derivation). It is more future-proof to separate these two unrelated concepts and will make it easier to implement channel types (see lightning/bolts#880).
The second commit adds an optional shutdown script to the channel's remote params, which will let us support
upfront_shutdown_script
later.NB: this is PR to the
only-store-remote-sigs
branch, to ensure we squash all channel codec changes into a single commit onmaster
.