mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
7feb8b21e1
In this commit, we update new Taproot related TLVs (nonces, partial sig, sig with nonce, etc). Along the way we were able to get rid of some boiler plate, but most importantly, we're able to better protect against API misuse (using a nonce that isn't initialized, etc) with the new options API. In some areas this introduces a bit of extra boiler plate, and where applicable I used some new helper functions to help cut down on the noise. Note to reviewers: this is done as a single commit, as changing the API breaks all callers, so if we want things to compile it needs to be in a wumbo commit.
237 lines
7.5 KiB
Go
237 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.
|
|
//
|
|
LocalNonce OptMusig2NonceTLV
|
|
|
|
// 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)
|
|
a.LocalNonce.WhenSome(func(localNonce Musig2NonceTLV) {
|
|
recordProducers = append(recordProducers, &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 (
|
|
dynHeight DynHeight
|
|
localNonce = a.LocalNonce.Zero()
|
|
)
|
|
typeMap, err := tlvRecords.ExtractRecords(
|
|
&localNonce, &dynHeight,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if val, ok := typeMap[a.LocalNonce.TlvType()]; ok && val == nil {
|
|
a.LocalNonce = tlv.SomeRecordT(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
|
|
}
|