lnd/contractcourt/taproot_briefcase.go
Olaoluwa Osuntokun a1788fe4a2
contractcourt: add new taproot briefcase to store ctrl block and tap tweaks
In this commit, we add a new taproot specific briefcase to store the
control block and tap tweaks for all taproot outputs. We chose this
route as many of the existing fields are serialized in line, so we
aren't able to serialize this new taproot specific information in the
existing briefcase.
2023-08-22 16:33:27 -07:00

690 lines
18 KiB
Go

package contractcourt
import (
"bytes"
"io"
"github.com/lightningnetwork/lnd/tlv"
)
const (
taprootCtrlBlockType tlv.Type = 0
taprootTapTweakType tlv.Type = 1
commitCtrlBlockType tlv.Type = 0
revokeCtrlBlockType tlv.Type = 1
outgoingHtlcCtrlBlockType tlv.Type = 2
incomingHtlcCtrlBlockType tlv.Type = 3
secondLevelCtrlBlockType tlv.Type = 4
anchorTapTweakType tlv.Type = 0
htlcTweakCtrlBlockType tlv.Type = 1
secondLevelHtlcTweakCtrlBlockType tlv.Type = 2
)
// tlvDecoder is an interface to support more generic decoding of TLV records.
type tlvDecoder interface {
// DecodeRecords returns a slice of TLV records that should be decoded.
DecodeRecords() []tlv.Record
// Decode decodes the given reader into the target struct.
Decode(r io.Reader) error
}
// makeAndDecodeStream takes a tlvDecoder and decodes the records encoded in
// the stream into it.
//
// TODO(roasbeef): use elsewhere
func makeAndDecodeStream(r io.Reader, d tlvDecoder) error {
stream, err := tlv.NewStream(d.DecodeRecords()...)
if err != nil {
return err
}
return stream.Decode(r)
}
// tlvEncoder is an interface to support more generic encoding of TLV records.
type tlvEncoder interface {
// EncodeRecords returns a slice of TLV records that should be encoded.
EncodeRecords() []tlv.Record
// Encode encodes the target struct into the given writer.
Encode(w io.Writer) error
}
// makeAnEncodeStream takes a tlvEncoder and encodes the records into the
// target writer as a TLV stream.
func makeAndEncodeStream[E tlvEncoder](w io.Writer, e E) error {
stream, err := tlv.NewStream(e.EncodeRecords()...)
if err != nil {
return err
}
return stream.Encode(w)
}
// taprootBriefcase is a supplemental storage struct that contains all the
// information we need to sweep taproot outputs.
type taprootBriefcase struct {
// CtrlBlock is the set of control block for the taproot outputs.
CtrlBlocks *ctrlBlocks
// TapTweaks is the set of taproot tweaks for the taproot outputs that
// are to be spent via a keyspend path. This includes anchors, and any
// revocation paths.
TapTweaks *tapTweaks
}
// newTaprootBriefcase returns a new instance of the taproot specific briefcase
// variant.
func newTaprootBriefcase() *taprootBriefcase {
return &taprootBriefcase{
CtrlBlocks: newCtrlBlocks(),
TapTweaks: newTapTweaks(),
}
}
// EncodeRecords returns a slice of TLV records that should be encoded.
func (t *taprootBriefcase) EncodeRecords() []tlv.Record {
return []tlv.Record{
newCtrlBlocksRecord(&t.CtrlBlocks),
newTapTweaksRecord(&t.TapTweaks),
}
}
// DecodeRecords returns a slice of TLV records that should be decoded.
func (t *taprootBriefcase) DecodeRecords() []tlv.Record {
return []tlv.Record{
newCtrlBlocksRecord(&t.CtrlBlocks),
newTapTweaksRecord(&t.TapTweaks),
}
}
// Encode records returns a slice of TLV records that should be encoded.
func (t *taprootBriefcase) Encode(w io.Writer) error {
stream, err := tlv.NewStream(t.EncodeRecords()...)
if err != nil {
return err
}
return stream.Encode(w)
}
// Decode decodes the given reader into the target struct.
func (t *taprootBriefcase) Decode(r io.Reader) error {
stream, err := tlv.NewStream(t.DecodeRecords()...)
if err != nil {
return err
}
return stream.Decode(r)
}
// resolverCtrlBlocks is a map of resolver IDs to their corresponding control block.
type resolverCtrlBlocks map[resolverID][]byte
// newResolverCtrlBlockss returns a new instance of the resolverCtrlBlocks
func newResolverCtrlBlocks() resolverCtrlBlocks {
return make(resolverCtrlBlocks)
}
// recordSize returns the size of the record in bytes.
func (r *resolverCtrlBlocks) recordSize() uint64 {
// Each record will be serialized as: <num_total_records> || <record>,
// where <record> is serialized as: <resolver_key> || <length> ||
// <ctrl_block>.
numBlocks := uint64(len(*r))
baseSize := tlv.VarIntSize(numBlocks)
recordSize := baseSize
for _, ctrlBlock := range *r {
recordSize += resolverIDLen
recordSize += tlv.VarIntSize(uint64(len(ctrlBlock)))
recordSize += uint64(len(ctrlBlock))
}
return recordSize
}
// Encode encodes the control blocks into the target writer.
func (r *resolverCtrlBlocks) Encode(w io.Writer) error {
numBlocks := uint64(len(*r))
var buf [8]byte
if err := tlv.WriteVarInt(w, numBlocks, &buf); err != nil {
return err
}
for id, ctrlBlock := range *r {
ctrlBlock := ctrlBlock
if _, err := w.Write(id[:]); err != nil {
return err
}
if err := varBytesEncoder(w, &ctrlBlock, &buf); err != nil {
return err
}
}
return nil
}
// Decode decodes the given reader into the target struct.
func (t *resolverCtrlBlocks) Decode(r io.Reader) error {
var buf [8]byte
numBlocks, err := tlv.ReadVarInt(r, &buf)
if err != nil {
return err
}
for i := uint64(0); i < numBlocks; i++ {
var id resolverID
if _, err := io.ReadFull(r, id[:]); err != nil {
return err
}
var ctrlBlock []byte
if err := varBytesDecoder(r, &ctrlBlock, &buf, 0); err != nil {
return err
}
(*t)[id] = ctrlBlock
}
return nil
}
// resolverCtrlBlocksEncoder is a custom TLV encoder for the resolverCtrlBlocks
func resolverCtrlBlocksEncoder(w io.Writer, val any, buf *[8]byte) error {
if typ, ok := val.(*resolverCtrlBlocks); ok {
return (*typ).Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "resolverCtrlBlocks")
}
// rsolverCtrlBlocksDecoder is a custom TLV decoder for the resolverCtrlBlocks
func resolverCtrlBlocksDecoder(r io.Reader, val any, buf *[8]byte,
l uint64) error {
if typ, ok := val.(*resolverCtrlBlocks); ok {
blockReader := io.LimitReader(r, int64(l))
resolverBlocks := newResolverCtrlBlocks()
err := resolverBlocks.Decode(blockReader)
if err != nil {
return err
}
*typ = resolverBlocks
return nil
}
return tlv.NewTypeForDecodingErr(val, "resolverCtrlBlocks", l, l)
}
// ctrlBlocks is the set of control blocks we need to sweep all the output for
// a taproot/musig2 channel.
type ctrlBlocks struct {
// CommitSweepCtrlBlock is the serialized control block needed to sweep
// our commitment output.
CommitSweepCtrlBlock []byte
// RevokeSweepCtrlBlock is the serialized control block that's used to
// sweep the reovked output of a breaching party.
RevokeSweepCtrlBlock []byte
// OutgoingHtlcCtrlBlocks is the set of serialized control blocks for
// all outgoing HTLCs. This is the set of HTLCs that we offered to the
// remote party. Depending on which commitment transaction was
// broadcast, we'll either sweep here and be done, or also need to go
// to the second level.
OutgoingHtlcCtrlBlocks resolverCtrlBlocks
// IncomingHtlcCtrlBlocks is the set of serialized control blocks for
// all incoming HTLCs
IncomingHtlcCtrlBlocks resolverCtrlBlocks
// SecondLevelCtrlBlocks is the set of serialized control blocks for
// need to sweep the second level HTLCs on our commitment transaction.
SecondLevelCtrlBlocks resolverCtrlBlocks
}
// newCtrlBlocks returns a new instance of the ctrlBlocks struct.
func newCtrlBlocks() *ctrlBlocks {
return &ctrlBlocks{
OutgoingHtlcCtrlBlocks: newResolverCtrlBlocks(),
IncomingHtlcCtrlBlocks: newResolverCtrlBlocks(),
SecondLevelCtrlBlocks: newResolverCtrlBlocks(),
}
}
// varBytesEncoder is a custom TLV encoder for a variable length byte slice.
func varBytesEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*[]byte); ok {
if err := tlv.WriteVarInt(w, uint64(len(*t)), buf); err != nil {
return err
}
return tlv.EVarBytes(w, t, buf)
}
return tlv.NewTypeForEncodingErr(val, "[]byte")
}
// varBytesDecoder is a custom TLV decoder for a variable length byte slice.
func varBytesDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(*[]byte); ok {
bytesLen, err := tlv.ReadVarInt(r, buf)
if err != nil {
return err
}
var bytes []byte
if err := tlv.DVarBytes(r, &bytes, buf, bytesLen); err != nil {
return err
}
*typ = bytes
return nil
}
return tlv.NewTypeForDecodingErr(val, "[]byte", l, l)
}
// ctrlBlockEncoder is a custom TLV encoder for the ctrlBlocks struct.
func ctrlBlockEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(**ctrlBlocks); ok {
return (*t).Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "ctrlBlocks")
}
// ctrlBlockDecoder is a custom TLV decoder for the ctrlBlocks struct.
func ctrlBlockDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(**ctrlBlocks); ok {
ctrlReader := io.LimitReader(r, int64(l))
var ctrlBlocks ctrlBlocks
err := ctrlBlocks.Decode(ctrlReader)
if err != nil {
return err
}
*typ = &ctrlBlocks
return nil
}
return tlv.NewTypeForDecodingErr(val, "ctrlBlocks", l, l)
}
// newCtrlBlocksRecord returns a new TLV record that can be used to
// encode/decode the set of cotrol blocks for the taproot outputs for a
// channel.
func newCtrlBlocksRecord(blks **ctrlBlocks) tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := ctrlBlockEncoder(&b, blks, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
taprootCtrlBlockType, blks, recordSize, ctrlBlockEncoder,
ctrlBlockDecoder,
)
}
// EncodeRecords returns the set of TLV records that encode the control block
// for the commitment transaction.
func (c *ctrlBlocks) EncodeRecords() []tlv.Record {
var records []tlv.Record
if len(c.CommitSweepCtrlBlock) > 0 {
records = append(records, tlv.MakePrimitiveRecord(
commitCtrlBlockType, &c.CommitSweepCtrlBlock,
))
}
if len(c.RevokeSweepCtrlBlock) > 0 {
records = append(records, tlv.MakePrimitiveRecord(
revokeCtrlBlockType, &c.RevokeSweepCtrlBlock,
))
}
if c.OutgoingHtlcCtrlBlocks != nil {
records = append(records, tlv.MakeDynamicRecord(
outgoingHtlcCtrlBlockType, &c.OutgoingHtlcCtrlBlocks,
c.OutgoingHtlcCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
))
}
if c.IncomingHtlcCtrlBlocks != nil {
records = append(records, tlv.MakeDynamicRecord(
incomingHtlcCtrlBlockType, &c.IncomingHtlcCtrlBlocks,
c.IncomingHtlcCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
))
}
if c.SecondLevelCtrlBlocks != nil {
records = append(records, tlv.MakeDynamicRecord(
secondLevelCtrlBlockType, &c.SecondLevelCtrlBlocks,
c.SecondLevelCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
))
}
return records
}
// DecodeRecords returns the set of TLV records that decode the control block.
func (c *ctrlBlocks) DecodeRecords() []tlv.Record {
return []tlv.Record{
tlv.MakePrimitiveRecord(
commitCtrlBlockType, &c.CommitSweepCtrlBlock,
),
tlv.MakePrimitiveRecord(
revokeCtrlBlockType, &c.RevokeSweepCtrlBlock,
),
tlv.MakeDynamicRecord(
outgoingHtlcCtrlBlockType, &c.OutgoingHtlcCtrlBlocks,
c.OutgoingHtlcCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
),
tlv.MakeDynamicRecord(
incomingHtlcCtrlBlockType, &c.IncomingHtlcCtrlBlocks,
c.IncomingHtlcCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
),
tlv.MakeDynamicRecord(
secondLevelCtrlBlockType, &c.SecondLevelCtrlBlocks,
c.SecondLevelCtrlBlocks.recordSize,
resolverCtrlBlocksEncoder, resolverCtrlBlocksDecoder,
),
}
}
// Record returns a TLV record that can be used to encode/decode the control
// blocks. type from a given TLV stream.
func (c *ctrlBlocks) Record() tlv.Record {
return tlv.MakePrimitiveRecord(commitCtrlBlockType, c)
}
// Encode encodes the set of control blocks.
func (c *ctrlBlocks) Encode(w io.Writer) error {
stream, err := tlv.NewStream(c.EncodeRecords()...)
if err != nil {
return err
}
return stream.Encode(w)
}
// Decode decodes the set of control blocks.
func (c *ctrlBlocks) Decode(r io.Reader) error {
stream, err := tlv.NewStream(c.DecodeRecords()...)
if err != nil {
return err
}
return stream.Decode(r)
}
// htlcTapTweakss maps an outpoint (the same format as the resolver ID) to the
// tap tweak needed to sweep a breached HTLC output. This is used for both the
// first and second level HTLC outputs.
type htlcTapTweaks map[resolverID][32]byte
// newHtlcTapTweaks returns a new instance of the htlcTapTweaks struct.
func newHtlcTapTweaks() htlcTapTweaks {
return make(htlcTapTweaks)
}
// recordSize returns the size of the record in bytes.
func (h *htlcTapTweaks) recordSize() uint64 {
// Each record will be serialized as: <num_tweaks> || <tweak>, where
// <tweak> is serialized as: <resolver_key> || <tweak>.
numTweaks := uint64(len(*h))
baseSize := tlv.VarIntSize(numTweaks)
recordSize := baseSize
for range *h {
// Each tweak is a fixed 32 bytes, so we just tally that an the
// size of the resolver ID.
recordSize += resolverIDLen
recordSize += 32
}
return recordSize
}
// Encode encodes the tap tweaks into the target writer.
func (h *htlcTapTweaks) Encode(w io.Writer) error {
numTweaks := uint64(len(*h))
var buf [8]byte
if err := tlv.WriteVarInt(w, numTweaks, &buf); err != nil {
return err
}
for id, tweak := range *h {
tweak := tweak
if _, err := w.Write(id[:]); err != nil {
return err
}
if _, err := w.Write(tweak[:]); err != nil {
return err
}
}
return nil
}
// htlcTapTweaksEncoder is a custom TLV encoder for the htlcTapTweaks struct.
func htlcTapTweaksEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*htlcTapTweaks); ok {
return (*t).Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "htlcTapTweaks")
}
// htlcTapTweaksDecoder is a custom TLV decoder for the htlcTapTweaks struct.
func htlcTapTweaksDecoder(r io.Reader, val any, buf *[8]byte,
l uint64) error {
if typ, ok := val.(*htlcTapTweaks); ok {
tweakReader := io.LimitReader(r, int64(l))
htlcTweaks := newHtlcTapTweaks()
err := htlcTweaks.Decode(tweakReader)
if err != nil {
return err
}
*typ = htlcTweaks
return nil
}
return tlv.NewTypeForDecodingErr(val, "htlcTapTweaks", l, l)
}
// Decode decodes the tap tweaks into the target struct.
func (h *htlcTapTweaks) Decode(reader io.Reader) error {
var buf [8]byte
numTweaks, err := tlv.ReadVarInt(reader, &buf)
if err != nil {
return err
}
for i := uint64(0); i < numTweaks; i++ {
var id resolverID
if _, err := io.ReadFull(reader, id[:]); err != nil {
return err
}
var tweak [32]byte
if _, err := io.ReadFull(reader, tweak[:]); err != nil {
return err
}
(*h)[id] = tweak
}
return nil
}
// tapTweaks stores the set of taptweaks needed to perform keyspends for the
// commitment outputs.
type tapTweaks struct {
// AnchorTweak is the tweak used to derive the key used to spend the anchor output.
AnchorTweak []byte
// BreachedHtlcTweaks stores the set of tweaks needed to sweep the
// revoked first level output of an HTLC.
BreachedHtlcTweaks htlcTapTweaks
// BreachedSecondLevelHtlcTweaks stores the set of tweaks needed to
// sweep the revoked *second* level output of an HTLC.
BreachedSecondLevelHltcTweaks htlcTapTweaks
}
// newTapTweaks returns a new tapTweaks struct.
func newTapTweaks() *tapTweaks {
return &tapTweaks{
BreachedHtlcTweaks: make(htlcTapTweaks),
BreachedSecondLevelHltcTweaks: make(htlcTapTweaks),
}
}
// tapTweaksEncoder is a custom TLV encoder for the tapTweaks struct.
func tapTweaksEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(**tapTweaks); ok {
return (*t).Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "tapTweaks")
}
// tapTweaksDecoder is a custom TLV decoder for the tapTweaks struct.
func tapTweaksDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(**tapTweaks); ok {
tweakReader := io.LimitReader(r, int64(l))
var tapTweaks tapTweaks
err := tapTweaks.Decode(tweakReader)
if err != nil {
return err
}
*typ = &tapTweaks
return nil
}
return tlv.NewTypeForDecodingErr(val, "tapTweaks", l, l)
}
// newTapTweaksRecord returns a new TLV record that can be used to
// encode/decode the tap tweak structs.
func newTapTweaksRecord(tweaks **tapTweaks) tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := tapTweaksEncoder(&b, tweaks, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
taprootTapTweakType, tweaks, recordSize, tapTweaksEncoder,
tapTweaksDecoder,
)
}
// EncodeRecords returns the set of TLV records that encode the tweaks.
func (t *tapTweaks) EncodeRecords() []tlv.Record {
var records []tlv.Record
if len(t.AnchorTweak) > 0 {
records = append(records, tlv.MakePrimitiveRecord(
anchorTapTweakType, &t.AnchorTweak,
))
}
if len(t.BreachedHtlcTweaks) > 0 {
records = append(records, tlv.MakeDynamicRecord(
htlcTweakCtrlBlockType, &t.BreachedHtlcTweaks,
t.BreachedHtlcTweaks.recordSize,
htlcTapTweaksEncoder, htlcTapTweaksDecoder,
))
}
if len(t.BreachedSecondLevelHltcTweaks) > 0 {
records = append(records, tlv.MakeDynamicRecord(
secondLevelHtlcTweakCtrlBlockType,
&t.BreachedSecondLevelHltcTweaks,
t.BreachedSecondLevelHltcTweaks.recordSize,
htlcTapTweaksEncoder, htlcTapTweaksDecoder,
))
}
return records
}
// DecodeRecords returns the set of TLV records that decode the tweaks.
func (t *tapTweaks) DecodeRecords() []tlv.Record {
return []tlv.Record{
tlv.MakePrimitiveRecord(anchorTapTweakType, &t.AnchorTweak),
tlv.MakeDynamicRecord(
htlcTweakCtrlBlockType, &t.BreachedHtlcTweaks,
t.BreachedHtlcTweaks.recordSize,
htlcTapTweaksEncoder, htlcTapTweaksDecoder,
),
tlv.MakeDynamicRecord(
secondLevelHtlcTweakCtrlBlockType,
&t.BreachedSecondLevelHltcTweaks,
t.BreachedSecondLevelHltcTweaks.recordSize,
htlcTapTweaksEncoder, htlcTapTweaksDecoder,
),
}
}
// Record returns a TLV record that can be used to encode/decode the tap
// tweaks.
func (t *tapTweaks) Record() tlv.Record {
return tlv.MakePrimitiveRecord(taprootTapTweakType, t)
}
// Encode encodes the set of tap tweaks.
func (t *tapTweaks) Encode(w io.Writer) error {
stream, err := tlv.NewStream(t.EncodeRecords()...)
if err != nil {
return err
}
return stream.Encode(w)
}
// Decode decodes the set of tap tweaks.
func (t *tapTweaks) Decode(r io.Reader) error {
stream, err := tlv.NewStream(t.DecodeRecords()...)
if err != nil {
return err
}
return stream.Decode(r)
}