Skip to content

multi: the great tlv wire migration#4747

Closed
Roasbeef wants to merge 38 commits into
lightningnetwork:masterfrom
Roasbeef:the-great-tlv-wire-migration
Closed

multi: the great tlv wire migration#4747
Roasbeef wants to merge 38 commits into
lightningnetwork:masterfrom
Roasbeef:the-great-tlv-wire-migration

Conversation

@Roasbeef
Copy link
Copy Markdown
Member

@Roasbeef Roasbeef commented Nov 5, 2020

This PR is a combination of #4099 and #3966 as they're interdependent and can't be merged in isolation yet leave master in a clean slate. The only new commit here is the last one that ensures the prior added migration uses the new protocol version to gate the "proper" behavior w.r.t always reading/writing the extra bytes for each migration.

Putting this up now so it can start building, there're a few relevant comments from the prior PRs that I'll address as fix up commits here. I know it looks large, but the bulk of the commits are mostly small changes in the lnwire package. If requested, I can squash in those 30 or so commits into a single one if it actually makes things easier to review.

First PR Description

In this Pr, we create a new ExtraOpaqueData based on the field with the same name that's present in all the announcement related messages. In later commits, we'll embed this new type in each message, so we'll have a generic way to add/parse TLV extensions from messages.

We then convert all the current known messages to include this new field, and parse it accordignly. The new ExtraOpaqueData also comes with some propsective helper methods for TLV encoding/decoding. I envision these will be used either within the top-level Encode/Decode methods, or by callers to populate new (optional) pointer fields that get added in the wire messages similar to the structs in htlcswitch/hop.

Second PR Description

In this commit, we modify the way we write the CommitSIg in certain locations to match the way it's encoded on the wire. With this change, we'll now properly add a length prefix in front of the message, meaning we'll be able to read the extra data included (if present), and still be able to serialize content after this message.

We add a simple migration to migrate all the current pending commitments, along with a test. We opt to do the simple enclose-copy method for the migration, which results in a larger diff, but IMO an easier to understand migration test.

With this change, errors from migrations will have the proper local line
number.
In this commit, we modify the way we write wire messages across the
entire database. We'll now ensure that we always write wire messages
with a length prefix. We update the `codec.go` file to always write a 2
byte length prefix, this affects the way we write the `CommitDiff` and
`LogUpdates` struct to disk. We also need to migrate the network results
bucket in the switch as it includes a wire message without a length
prefix.
In this commit, we create a new `ExtraOpaqueData` based on the field
with the same name that's present in all the announcement related
messages. In later commits, we'll embed this new type in each message,
so we'll have a generic way to add/parse TLV extensions from messages.
In order to prep for allowing TLV extensions for the `ReplyChannelRange`
and `QueryChannelRange` messages, we'll need to remove the struct
embedding as is. If we don't remove this, then we'll attempt to decode
TLV extensions from both the embedded and outer struct.

All relevant call sites have been updated to reflect this minor change.
In this commit, we convert the delivery address in the open and accept
channel methods to be a TLV type. This works as an "empty" delivery
address is encoded using a two zero bytes (uint16 length zero), and a
tlv type of 0 is encoded in the same manner (byte for type, byte for
zero length). This change allows us to easily extend these messages in
the future, in a uniform manner.
In this commit, we update a series of unit tests in the code base to now
pass due to the new wire message encode/decode logic. In many instances,
we'll now manually set the extra bytes to an empty byte slice to avoid
comparisons that fail due to one message having an empty byte slice and
the other having a nil pointer.
…rsion for TLV

In this commit, we update all the existing methods for encoding and
decoding in the package to also observe the currently unused `pver`
(protocol version) argument. With this change, we can enforce the new
"TLV everywhere" rules for all the new code, while leaving the existing
migrations untouched (using the legacy version). Otherwise, a legacy
migration would possibly read too many bytes (as the ExtraOpaqueBytes
reads until EOF), causing it to corrupt existing data on disk.

The Encode/Decode methods of the ExtraOpaqueData struct will now observe
the new `pver` value, and return early (not reading/writing) the extra
bytes based on this value.
In this commit, update the encode/decode code concerning wire messages
in the existing migrations to force them to specify that they want the
legacy protocol version which doesn't attempt to read the extra bytes
for all messages other than gossip messages.
This is requires to ensure the migration is able to run without issue as
the stored contents won't contain any optional TLV data.
@Roasbeef Roasbeef added this to the 0.12.0 milestone Nov 5, 2020
@Roasbeef Roasbeef added database Related to the database/storage of LND v0.12 wire protocol Encoding of messages for the communication between nodes labels Nov 5, 2020
@Roasbeef Roasbeef removed the request for review from wpaulino November 5, 2020 02:50
Copy link
Copy Markdown
Contributor

@halseth halseth left a comment

Choose a reason for hiding this comment

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

Great PR, not too bad to review even though it is large.

My main concern is the usage of lnwire within the old migrations. I would be more confident if we just copied the old code we need in there before we start changing the behavior oflnwire

// openChan -> nodeID -> chanPoint
//
// TODO(roasbeef): flesh out comment
openChannelBucket = []byte("open-chan-bucket")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

move keys to the migration itself.


closedChanBucket := tx.ReadWriteBucket(closedChannelBucket)
if closedChannelBucket == nil {
return fmt.Errorf("no closed channels found")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could this happen? Maybe the error message should be changed to say the bucket was not found.

Comment thread channeldb/codec.go
return err
}

msgLen := uint16(len(msgBuf.Bytes()))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use uint32 or uint64 to allow longer messages in the future?

Comment thread lnwire/revoke_and_ack.go
func NewRevokeAndAck() *RevokeAndAck {
return &RevokeAndAck{}
return &RevokeAndAck{
ExtraData: make([]byte, 0),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why this change? Not done for other messages.

Comment thread lnwire/init_message.go
return &Init{
GlobalFeatures: gf,
Features: f,
ExtraData: make([]byte, 0),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same question, why is this done?

Comment thread lnwire/message.go

// ProtocolVersionTLV is the current modern protocol version. When
// reading/writing messages with this version, decoding will continue
// until the entire payload has been ready. When writing with this
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

typo: ready

Comment thread lnwire/extra_bytes.go
func (e *ExtraOpaqueData) Decode(r io.Reader, pver uint32) error {
// Only if we're using the modern protocl version will we attempt to
// keep on decoding past the end of the "main message".
if pver != ProtocolVersionTLV {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you point to the place in the migrations where this was a problem? I'm trying to understand why this is neccassary.

This also restates that the migrations shouldn't be depending on code that might change from underneath them. I would prefer rather than this change, copying the legacy lnwire encoding into the migrations, so we won't have to worry about this later.

// Serialize the message with its wire encoding.
var b bytes.Buffer
if _, err := lnwire.WriteMessage(&b, msg, 0); err != nil {
_, err := lnwire.WriteMessage(&b, msg, lnwire.ProtocolVersionTLV)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldn't this be legacy?

Comment thread lnwire/extra_bytes.go
// set of extra opaque data.
func (e *ExtraOpaqueData) Decode(r io.Reader) error {
func (e *ExtraOpaqueData) Decode(r io.Reader, pver uint32) error {
// Only if we're using the modern protocl version will we attempt to
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

typo: protocl

Comment thread lnwire/extra_bytes.go
// Only if we're using the modern protocl version will we attempt to
// keep on decoding past the end of the "main message".
if pver != ProtocolVersionTLV {
return nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

also, doesn't this change the behavior for the legacy protocol? Before this PR the messages that had ExtraOpaqueData defined would be read to the end, while after this we will stop reading (so if migrations are done for these messages, that data won't be copied).

@Roasbeef Roasbeef modified the milestones: 0.12.0, 0.13.0 Dec 12, 2020
@halseth
Copy link
Copy Markdown
Contributor

halseth commented Jan 15, 2021

Replaced by #4923

@halseth halseth closed this Jan 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

database Related to the database/storage of LND v0.12 wire protocol Encoding of messages for the communication between nodes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants