lnd/lnwire/channel_reestablish.go

236 lines
7.5 KiB
Go

package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/tlv"
)
const (
CRDynHeight tlv.Type = 20
)
// DynHeight is a newtype wrapper to get the proper RecordProducer instance
// to smoothly integrate with the ChannelReestablish Message instance.
type DynHeight uint64
// Record implements the RecordProducer interface, allowing a full tlv.Record
// object to be constructed from a DynHeight.
func (d *DynHeight) Record() tlv.Record {
return tlv.MakePrimitiveRecord(CRDynHeight, (*uint64)(d))
}
// ChannelReestablish is a message sent between peers that have an existing
// open channel upon connection reestablishment. This message allows both sides
// to report their local state, and their current knowledge of the state of the
// remote commitment chain. If a deviation is detected and can be recovered
// from, then the necessary messages will be retransmitted. If the level of
// desynchronization is irreconcilable, then the channel will be force closed.
type ChannelReestablish struct {
// ChanID is the channel ID of the channel state we're attempting to
// synchronize with the remote party.
ChanID ChannelID
// NextLocalCommitHeight is the next local commitment height of the
// sending party. If the height of the sender's commitment chain from
// the receiver's Pov is one less that this number, then the sender
// should re-send the *exact* same proposed commitment.
//
// In other words, the receiver should re-send their last sent
// commitment iff:
//
// * NextLocalCommitHeight == remoteCommitChain.Height
//
// This covers the case of a lost commitment which was sent by the
// sender of this message, but never received by the receiver of this
// message.
NextLocalCommitHeight uint64
// RemoteCommitTailHeight is the height of the receiving party's
// unrevoked commitment from the PoV of the sender of this message. If
// the height of the receiver's commitment is *one more* than this
// value, then their prior RevokeAndAck message should be
// retransmitted.
//
// In other words, the receiver should re-send their last sent
// RevokeAndAck message iff:
//
// * localCommitChain.tail().Height == RemoteCommitTailHeight + 1
//
// This covers the case of a lost revocation, wherein the receiver of
// the message sent a revocation for a prior state, but the sender of
// the message never fully processed it.
RemoteCommitTailHeight uint64
// LastRemoteCommitSecret is the last commitment secret that the
// receiving node has sent to the sending party. This will be the
// secret of the last revoked commitment transaction. Including this
// provides proof that the sending node at least knows of this state,
// as they couldn't have produced it if it wasn't sent, as the value
// can be authenticated by querying the shachain or the receiving
// party.
LastRemoteCommitSecret [32]byte
// LocalUnrevokedCommitPoint is the commitment point used in the
// current un-revoked commitment transaction of the sending party.
LocalUnrevokedCommitPoint *btcec.PublicKey
// LocalNonce is an optional field that stores a local musig2 nonce.
// This will only be populated if the simple taproot channels type was
// negotiated.
//
// TODO(roasbeef): rename to verification nonce
LocalNonce *Musig2Nonce
// DynHeight is an optional field that stores the dynamic commitment
// negotiation height that is incremented upon successful completion of
// a dynamic commitment negotiation
DynHeight fn.Option[DynHeight]
// ExtraData is the set of data that was appended to this message to
// fill out the full maximum transport message size. These fields can
// be used to specify optional data such as custom TLV fields.
ExtraData ExtraOpaqueData
}
// A compile time check to ensure ChannelReestablish implements the
// lnwire.Message interface.
var _ Message = (*ChannelReestablish)(nil)
// Encode serializes the target ChannelReestablish into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *ChannelReestablish) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteChannelID(w, a.ChanID); err != nil {
return err
}
if err := WriteUint64(w, a.NextLocalCommitHeight); err != nil {
return err
}
if err := WriteUint64(w, a.RemoteCommitTailHeight); err != nil {
return err
}
// If the commit point wasn't sent, then we won't write out any of the
// remaining fields as they're optional.
if a.LocalUnrevokedCommitPoint == nil {
// However, we'll still write out the extra data if it's
// present.
//
// NOTE: This is here primarily for the quickcheck tests, in
// practice, we'll always populate this field.
return WriteBytes(w, a.ExtraData)
}
// Otherwise, we'll write out the remaining elements.
if err := WriteBytes(w, a.LastRemoteCommitSecret[:]); err != nil {
return err
}
if err := WritePublicKey(w, a.LocalUnrevokedCommitPoint); err != nil {
return err
}
recordProducers := make([]tlv.RecordProducer, 0, 1)
if a.LocalNonce != nil {
recordProducers = append(recordProducers, a.LocalNonce)
}
a.DynHeight.WhenSome(func(h DynHeight) {
recordProducers = append(recordProducers, &h)
})
err := EncodeMessageExtraData(&a.ExtraData, recordProducers...)
if err != nil {
return err
}
return WriteBytes(w, a.ExtraData)
}
// Decode deserializes a serialized ChannelReestablish stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (a *ChannelReestablish) Decode(r io.Reader, pver uint32) error {
err := ReadElements(r,
&a.ChanID,
&a.NextLocalCommitHeight,
&a.RemoteCommitTailHeight,
)
if err != nil {
return err
}
// This message has currently defined optional fields. As a result,
// we'll only proceed if there's still bytes remaining within the
// reader.
//
// We'll manually parse out the optional fields in order to be able to
// still utilize the io.Reader interface.
// We'll first attempt to read the optional commit secret, if we're at
// the EOF, then this means the field wasn't included so we can exit
// early.
var buf [32]byte
_, err = io.ReadFull(r, buf[:32])
if err == io.EOF {
// If there aren't any more bytes, then we'll emplace an empty
// extra data to make our quickcheck tests happy.
a.ExtraData = make([]byte, 0)
return nil
} else if err != nil {
return err
}
// If the field is present, then we'll copy it over and proceed.
copy(a.LastRemoteCommitSecret[:], buf[:])
// We'll conclude by parsing out the commitment point. We don't check
// the error in this case, as it has included the commit secret, then
// they MUST also include the commit point.
if err = ReadElement(r, &a.LocalUnrevokedCommitPoint); err != nil {
return err
}
var tlvRecords ExtraOpaqueData
if err := ReadElements(r, &tlvRecords); err != nil {
return err
}
var localNonce Musig2Nonce
var dynHeight DynHeight
typeMap, err := tlvRecords.ExtractRecords(
&localNonce, &dynHeight,
)
if err != nil {
return err
}
if val, ok := typeMap[NonceRecordType]; ok && val == nil {
a.LocalNonce = &localNonce
}
if val, ok := typeMap[CRDynHeight]; ok && val == nil {
a.DynHeight = fn.Some(dynHeight)
}
if len(tlvRecords) != 0 {
a.ExtraData = tlvRecords
}
return nil
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (a *ChannelReestablish) MsgType() MessageType {
return MsgChannelReestablish
}