-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
lnwire: add wire types for dynamic commitments #8026
lnwire: add wire types for dynamic commitments #8026
Conversation
38589cd
to
1938b2b
Compare
7ec0860
to
a48aa9a
Compare
lnwire/dyn_begin_propose.go
Outdated
// | ||
// This is a part of the lnwire.Message interface. | ||
func (dbp *DynBeginPropose) Encode(w *bytes.Buffer, _ uint32) error { | ||
if err := WriteChannelID(w, dbp.ChanID); err != nil { |
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.
We should still read in the ExtraOpaqueData
here. Otherwise we may end up with just a partially consumed buffer.
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 guess we also still need to decide which portions of the message are TLV vs otherwise.
Think this should come with fuzz tests for the new message types. Can just do round-trip equality w/ decode + encode |
a48aa9a
to
3b39bc8
Compare
It does. |
3b39bc8
to
6ada649
Compare
The more modern fuzz tests are in Lines 26 to 54 in ca9cdac
|
4dc6cba
to
32601d4
Compare
889dd1f
to
81e3e83
Compare
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.
Message structure looks pretty good.
Open to trying out Option[T]
in place of the pointer usage as an initial pilot on the cobdebase. Here's one I prototyped in the past: https://go.dev/play/p/l1R_ueg2NoJ. I think siimlar to tapd
, we'd add an fn
package, drop this in, then go from there.
Other thing we need to nail down is the initial version of the reject bitvector/message we want go along with.
// the initiator of a dynamic commitment negotiation or the responder | ||
// of a dynamic commitment negotiation. bool true indicates it is the | ||
// initiator | ||
Initiator bool |
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.
Isn't this information already apparent to both sides contextually?
Eg: for channel funding we know who initiator is from the very start, so we don't need messages like closing_signed
to signal if it's sent by the initiator not (for that msg behavior differs based on if you're the initiator or not).
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.
What do we do in the situation where both sides send a dyn_propose
"at the same time"? It's unlikely in practice but possible. I suppose we could just fail at some point when the conflict becomes unignorable. This was put in to express intent to the receiver that we believe we are initiating the negotiation. Such an intent isn't clear otherwise.
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 think this can be fixed if you abort the flow if you send a dyn_propose
with initiator=true
and receive a dyn_propose
with initiator=true
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. But absent the initiator=true label I don't think this can be accomplished.
var csvDelayScratch uint16 | ||
csvDelay := tlv.MakePrimitiveRecord(DPToSelfDelay, &csvDelayScratch) | ||
|
||
var maxHtlcsScratch uint16 |
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.
If the target isn't a non-primitive type, then you can decode directly into it. Should be able to save ~20 lines or so here w/ that.
lnwire/dyn_reject.go
Outdated
var csvDelayScratch uint16 | ||
csvDelay := tlv.MakePrimitiveRecord(DPToSelfDelay, &csvDelayScratch) | ||
|
||
var maxHtlcsScratch uint16 |
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.
Similar here re if not primitive type, can pass the attribute directly in.
@Crypt-iQ: review reminder |
81e3e83
to
7e00fe0
Compare
case *RawFeatureVector: | ||
f := NewRawFeatureVector() | ||
err = f.Decode(r) | ||
if err != nil { | ||
return err | ||
} | ||
*e = *f |
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.
Is this a sin?
1ec5cce
to
ecf9483
Compare
lnwire/dyn_propose.go
Outdated
}) | ||
dp.FundingKey.WhenSome(func(key btcec.PublicKey) { | ||
keyScratch := &key | ||
tlvRecords = append(tlvRecords, tlv.MakePrimitiveRecord( |
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: should have a line break before tlvRecords
similar to how ChannelType
is below
) | ||
}) | ||
dp.KickoffFeerate.WhenSome(func(kickoffFeerate chainfee.SatPerKWeight) { | ||
protoSats := uint32(kickoffFeerate) |
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.
Note for later implementation:
kickoffFeerate
is SatPerKWeight
which is int64
under the hood. Casting to uint32
can lead to integer truncation if the feerate is very high or low. To avoid headaches and possible bugs, we should ensure that whatever kickoff feerate we send/receive is <= math.MaxUint32
lnwire/dyn_propose.go
Outdated
dp.ChannelType = fn.Some(chanTypeScratch) | ||
} | ||
if val, ok := typeMap[DPKickoffFeerate]; ok && val == nil { | ||
dp.KickoffFeerate = fn.Some(chainfee.SatPerKWeight( |
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: newline for chainfee.SatPerKWeight
to avoid double parenthesis
lnwire/lnwire_test.go
Outdated
@@ -708,6 +710,100 @@ func TestLightningWireProtocol(t *testing.T) { | |||
|
|||
v[0] = reflect.ValueOf(req) | |||
}, | |||
MsgDynPropose: func(v []reflect.Value, r *rand.Rand) { | |||
var dp DynPropose | |||
var id ChannelID |
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: id
unnecessary?
lnwire/lnwire_test.go
Outdated
}, | ||
MsgDynReject: func(v []reflect.Value, r *rand.Rand) { | ||
var dr DynReject | ||
var id ChannelID |
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: id
unnecessary?
this commit introduces many of the most common functions you will want to use with the Option type. Not all of them are used immediately in this PR.
00fc24c
to
134fa78
Compare
134fa78
to
80c3368
Compare
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.
LGTM ✏️
|
||
// Option[A] represents a value which may or may not be there. This is very | ||
// often preferable to nil-able pointers. | ||
type Option[A any] struct { |
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.
🤩
lnwire/channel_reestablish.go
Outdated
// DynHeight is an optional field that stores the dynamic commitment | ||
// negotiation height that is incremented upon successful completion of | ||
// a dynamic commitment negotiation | ||
DynHeight *DynHeight |
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.
Could be an optional?
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.
Fixed. Should I also do the same thing to the LocalNonce field even though it's unrelated to dyncomms or leave it heterogeneous for now?
80c3368
to
b8ea14b
Compare
lnwire: add tests for dynamic commitment wire serialization
b8ea14b
to
134e9a7
Compare
Change Description
This PR introduces new datatypes to LND as described in the Dynamic Commitments spec.
Steps to Test
make unit
Pull Request Checklist
Testing
Code Style and Documentation
[skip ci]
in the commit message for small changes.📝 Please see our Contribution Guidelines for further guidance.