lnd/lnwire/accept_channel.go
Johan T. Halseth 6842c0ba8c
lnwire: convert the delivery addr in [Open+Accept]Channel to a TLV type in ExtraData
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.

When decoding the message we snip the bytes from the read TLV data.
Similarly, when encoding we concatenate the TLV record for the shutdown
script with the rest of the TLV data.
2021-02-24 17:40:08 +01:00

265 lines
8.8 KiB
Go

package lnwire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
)
// AcceptChannel is the message Bob sends to Alice after she initiates the
// single funder channel workflow via an AcceptChannel message. Once Alice
// receives Bob's response, then she has all the items necessary to construct
// the funding transaction, and both commitment transactions.
type AcceptChannel struct {
// PendingChannelID serves to uniquely identify the future channel
// created by the initiated single funder workflow.
PendingChannelID [32]byte
// DustLimit is the specific dust limit the sender of this message
// would like enforced on their version of the commitment transaction.
// Any output below this value will be "trimmed" from the commitment
// transaction, with the amount of the HTLC going to dust.
DustLimit btcutil.Amount
// MaxValueInFlight represents the maximum amount of coins that can be
// pending within the channel at any given time. If the amount of funds
// in limbo exceeds this amount, then the channel will be failed.
MaxValueInFlight MilliSatoshi
// ChannelReserve is the amount of BTC that the receiving party MUST
// maintain a balance above at all times. This is a safety mechanism to
// ensure that both sides always have skin in the game during the
// channel's lifetime.
ChannelReserve btcutil.Amount
// HtlcMinimum is the smallest HTLC that the sender of this message
// will accept.
HtlcMinimum MilliSatoshi
// MinAcceptDepth is the minimum depth that the initiator of the
// channel should wait before considering the channel open.
MinAcceptDepth uint32
// CsvDelay is the number of blocks to use for the relative time lock
// in the pay-to-self output of both commitment transactions.
CsvDelay uint16
// MaxAcceptedHTLCs is the total number of incoming HTLC's that the
// sender of this channel will accept.
//
// TODO(roasbeef): acks the initiator's, same with max in flight?
MaxAcceptedHTLCs uint16
// FundingKey is the key that should be used on behalf of the sender
// within the 2-of-2 multi-sig output that it contained within the
// funding transaction.
FundingKey *btcec.PublicKey
// RevocationPoint is the base revocation point for the sending party.
// Any commitment transaction belonging to the receiver of this message
// should use this key and their per-commitment point to derive the
// revocation key for the commitment transaction.
RevocationPoint *btcec.PublicKey
// PaymentPoint is the base payment point for the sending party. This
// key should be combined with the per commitment point for a
// particular commitment state in order to create the key that should
// be used in any output that pays directly to the sending party, and
// also within the HTLC covenant transactions.
PaymentPoint *btcec.PublicKey
// DelayedPaymentPoint is the delay point for the sending party. This
// key should be combined with the per commitment point to derive the
// keys that are used in outputs of the sender's commitment transaction
// where they claim funds.
DelayedPaymentPoint *btcec.PublicKey
// HtlcPoint is the base point used to derive the set of keys for this
// party that will be used within the HTLC public key scripts. This
// value is combined with the receiver's revocation base point in order
// to derive the keys that are used within HTLC scripts.
HtlcPoint *btcec.PublicKey
// FirstCommitmentPoint is the first commitment point for the sending
// party. This value should be combined with the receiver's revocation
// base point in order to derive the revocation keys that are placed
// within the commitment transaction of the sender.
FirstCommitmentPoint *btcec.PublicKey
// UpfrontShutdownScript is the script to which the channel funds should
// be paid when mutually closing the channel. This field is optional, and
// and has a length prefix, so a zero will be written if it is not set
// and its length followed by the script will be written if it is set.
UpfrontShutdownScript DeliveryAddress
// 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.
//
// NOTE: Since the upfront shutdown script MUST be present (though can
// be zero-length) if any TLV data is available, the script will be
// extracted and removed from this blob when decoding. ExtraData will
// contain all TLV records _except_ the DeliveryAddress record in that
// case.
ExtraData ExtraOpaqueData
}
// A compile time check to ensure AcceptChannel implements the lnwire.Message
// interface.
var _ Message = (*AcceptChannel)(nil)
// Encode serializes the target AcceptChannel into the passed io.Writer
// implementation. Serialization will observe the rules defined by the passed
// protocol version.
//
// This is part of the lnwire.Message interface.
func (a *AcceptChannel) Encode(w io.Writer, pver uint32) error {
// Since the upfront script is encoded as a TLV record, concatenate it
// with the ExtraData, and write them as one.
tlvRecords, err := packShutdownScript(
a.UpfrontShutdownScript, a.ExtraData,
)
if err != nil {
return err
}
return WriteElements(w,
a.PendingChannelID[:],
a.DustLimit,
a.MaxValueInFlight,
a.ChannelReserve,
a.HtlcMinimum,
a.MinAcceptDepth,
a.CsvDelay,
a.MaxAcceptedHTLCs,
a.FundingKey,
a.RevocationPoint,
a.PaymentPoint,
a.DelayedPaymentPoint,
a.HtlcPoint,
a.FirstCommitmentPoint,
tlvRecords,
)
}
// Decode deserializes the serialized AcceptChannel stored in the passed
// io.Reader into the target AcceptChannel using the deserialization rules
// defined by the passed protocol version.
//
// This is part of the lnwire.Message interface.
func (a *AcceptChannel) Decode(r io.Reader, pver uint32) error {
// Read all the mandatory fields in the accept message.
err := ReadElements(r,
a.PendingChannelID[:],
&a.DustLimit,
&a.MaxValueInFlight,
&a.ChannelReserve,
&a.HtlcMinimum,
&a.MinAcceptDepth,
&a.CsvDelay,
&a.MaxAcceptedHTLCs,
&a.FundingKey,
&a.RevocationPoint,
&a.PaymentPoint,
&a.DelayedPaymentPoint,
&a.HtlcPoint,
&a.FirstCommitmentPoint,
)
if err != nil {
return err
}
// For backwards compatibility, the optional extra data blob for
// AcceptChannel must contain an entry for the upfront shutdown script.
// We'll read it out and attempt to parse it.
var tlvRecords ExtraOpaqueData
if err := ReadElements(r, &tlvRecords); err != nil {
return err
}
a.UpfrontShutdownScript, a.ExtraData, err = parseShutdownScript(
tlvRecords,
)
if err != nil {
return err
}
return nil
}
// packShutdownScript takes an upfront shutdown script and an opaque data blob
// and concatenates them.
func packShutdownScript(addr DeliveryAddress, extraData ExtraOpaqueData) (
ExtraOpaqueData, error) {
// We'll always write the upfront shutdown script record, regardless of
// the script being empty.
var tlvRecords ExtraOpaqueData
// Pack it into a data blob as a TLV record.
err := tlvRecords.PackRecords(addr.NewRecord())
if err != nil {
return nil, fmt.Errorf("unable to pack upfront shutdown "+
"script as TLV record: %v", err)
}
// Concatenate the remaining blob with the shutdown script record.
tlvRecords = append(tlvRecords, extraData...)
return tlvRecords, nil
}
// parseShutdownScript reads and extract the upfront shutdown script from the
// passe data blob. It returns the script, if any, and the remainder of the
// data blob.
//
// This can be used to parse extra data for the OpenChannel and AcceptChannel
// messages, where the shutdown script is mandatory if extra TLV data is
// present.
func parseShutdownScript(tlvRecords ExtraOpaqueData) (DeliveryAddress,
ExtraOpaqueData, error) {
// If no TLV data is present there can't be any script available.
if len(tlvRecords) == 0 {
return nil, tlvRecords, nil
}
// Otherwise the shutdown script MUST be present.
var addr DeliveryAddress
tlvs, err := tlvRecords.ExtractRecords(addr.NewRecord())
if err != nil {
return nil, nil, err
}
// Not among TLV records, this means the data was invalid.
if _, ok := tlvs[DeliveryAddrType]; !ok {
return nil, nil, fmt.Errorf("no shutdown script in non-empty " +
"data blob")
}
// Now that we have retrieved the address (which can be zero-length),
// we'll remove the bytes encoding it from the TLV data before
// returning it.
addrLen := len(addr)
tlvRecords = tlvRecords[addrLen+2:]
return addr, tlvRecords, nil
}
// MsgType returns the MessageType code which uniquely identifies this message
// as an AcceptChannel on the wire.
//
// This is part of the lnwire.Message interface.
func (a *AcceptChannel) MsgType() MessageType {
return MsgAcceptChannel
}
// MaxPayloadLength returns the maximum allowed payload length for a
// AcceptChannel message.
//
// This is part of the lnwire.Message interface.
func (a *AcceptChannel) MaxPayloadLength(uint32) uint32 {
return MaxMsgBody
}