diff --git a/lnwire/partial_sig.go b/lnwire/partial_sig.go new file mode 100644 index 000000000..fa05950ab --- /dev/null +++ b/lnwire/partial_sig.go @@ -0,0 +1,201 @@ +package lnwire + +import ( + "io" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // PartialSigLen is the length of a musig2 partial signature. + PartialSigLen = 32 + + // PartialSigRecordType is the type of the tlv record for a musig2 + // partial signature. This is an _even_ type, which means it's required + // if included. + PartialSigRecordType tlv.Type = 6 +) + +// PartialSig is the base partial sig type. This only encodes the 32-byte +// partial signature. This is used for the co-op close flow, as both sides have +// already exchanged nonces, so they can send just the partial signature. +type PartialSig struct { + // Sig is the 32-byte musig2 partial signature. + Sig btcec.ModNScalar +} + +// NewPartialSig creates a new partial sig. +func NewPartialSig(sig btcec.ModNScalar) PartialSig { + return PartialSig{ + Sig: sig, + } +} + +// Record returns the tlv record for the partial sig. +func (p *PartialSig) Record() tlv.Record { + return tlv.MakeStaticRecord( + PartialSigRecordType, p, PartialSigLen, + partialSigTypeEncoder, partialSigTypeDecoder, + ) +} + +// partialSigTypeEncoder encodes a 32-byte musig2 partial signature as a TLV +// value. +func partialSigTypeEncoder(w io.Writer, val interface{}, buf *[8]byte) error { + if v, ok := val.(*PartialSig); ok { + sigBytes := v.Sig.Bytes() + + return tlv.EBytes32(w, &sigBytes, buf) + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.PartialSig") +} + +// Encode writes the encoded version of this message to the passed io.Writer. +func (p *PartialSig) Encode(w io.Writer) error { + return partialSigTypeEncoder(w, p, nil) +} + +// partialSigTypeDecoder decodes a 32-byte musig2 extended partial signature. +func partialSigTypeDecoder(r io.Reader, val interface{}, buf *[8]byte, + l uint64) error { + + if v, ok := val.(*PartialSig); ok && l == PartialSigLen { + var sBytes [32]byte + err := tlv.DBytes32(r, &sBytes, buf, PartialSigLen) + if err != nil { + return err + } + + var s btcec.ModNScalar + s.SetBytes(&sBytes) + + *v = PartialSig{ + Sig: s, + } + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "lnwire.PartialSig", l, + PartialSigLen) +} + +// Decode reads the encoded version of this message from the passed io.Reader. +func (p *PartialSig) Decode(r io.Reader) error { + return partialSigTypeDecoder(r, p, nil, PartialSigLen) +} + +const ( + // PartialSigWithNonceLen is the length of a serialized + // PartialSigWithNonce. The sig is encoded as the 32 byte S value + // followed by the 66 nonce value. + PartialSigWithNonceLen = 98 + + // PartialSigWithNonceRecordType is the type of the tlv record for a + // musig2 partial signature with nonce. This is an _even_ type, which + // means it's required if included. + PartialSigWithNonceRecordType tlv.Type = 2 +) + +// PartialSigWithNonce is a partial signature with the nonce that was used to +// generate the signature. This is used for funding as well as the commitment +// transaction update dance. By sending the nonce only with the signature, we +// enable the sender to generate their nonce just before they create their +// signature. Signers can use this trait to mix in additional contextual data +// such as the commitment txn itself into their nonce generation function. +// +// The final signature is 98 bytes: 32 bytes for the S value, and 66 bytes for +// the public nonce (two compressed points). +type PartialSigWithNonce struct { + PartialSig + + // Nonce is the 66-byte musig2 nonce. + Nonce Musig2Nonce +} + +// NewPartialSigWithNonce creates a new partial sig with nonce. +func NewPartialSigWithNonce(nonce [musig2.PubNonceSize]byte, + sig btcec.ModNScalar) *PartialSigWithNonce { + + return &PartialSigWithNonce{ + Nonce: nonce, + PartialSig: NewPartialSig(sig), + } +} + +// Record returns the tlv record for the partial sig with nonce. +func (p *PartialSigWithNonce) Record() tlv.Record { + return tlv.MakeStaticRecord( + PartialSigWithNonceRecordType, p, PartialSigWithNonceLen, + partialSigWithNonceTypeEncoder, partialSigWithNonceTypeDecoder, + ) +} + +// partialSigWithNonceTypeEncoder encodes 98-byte musig2 extended partial +// signature as: s {32} || nonce {66}. +func partialSigWithNonceTypeEncoder(w io.Writer, val interface{}, + buf *[8]byte) error { + + if v, ok := val.(*PartialSigWithNonce); ok { + sigBytes := v.Sig.Bytes() + if _, err := w.Write(sigBytes[:]); err != nil { + return err + } + if _, err := w.Write(v.Nonce[:]); err != nil { + return err + } + + return nil + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.PartialSigWithNonce") +} + +// Encode writes the encoded version of this message to the passed io.Writer. +func (p *PartialSigWithNonce) Encode(w io.Writer) error { + return partialSigWithNonceTypeEncoder(w, p, nil) +} + +// partialSigWithNonceTypeDecoder decodes a 98-byte musig2 extended partial +// signature. +func partialSigWithNonceTypeDecoder(r io.Reader, val interface{}, buf *[8]byte, + l uint64) error { + + if v, ok := val.(*PartialSigWithNonce); ok && + l == PartialSigWithNonceLen { + + var sBytes [32]byte + err := tlv.DBytes32(r, &sBytes, buf, PartialSigLen) + if err != nil { + return err + } + + var s btcec.ModNScalar + s.SetBytes(&sBytes) + + var nonce [66]byte + if _, err := io.ReadFull(r, nonce[:]); err != nil { + return err + } + + *v = PartialSigWithNonce{ + PartialSig: NewPartialSig(s), + Nonce: nonce, + } + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "lnwire.PartialSigWithNonce", l, + PartialSigWithNonceLen) +} + +// Decode reads the encoded version of this message from the passed io.Reader. +func (p *PartialSigWithNonce) Decode(r io.Reader) error { + return partialSigWithNonceTypeDecoder( + r, p, nil, PartialSigWithNonceLen, + ) +} diff --git a/lnwire/writer_test.go b/lnwire/writer_test.go index 185685c41..ccdeabcf6 100644 --- a/lnwire/writer_test.go +++ b/lnwire/writer_test.go @@ -160,7 +160,9 @@ func TestWriteSig(t *testing.T) { func TestWriteSigs(t *testing.T) { buf := new(bytes.Buffer) - sig1, sig2, sig3 := Sig{bytes: [64]byte{1}}, Sig{bytes: [64]byte{2}}, Sig{bytes: [64]byte{3}} + sig1 := Sig{bytes: [64]byte{1}} + sig2 := Sig{bytes: [64]byte{2}} + sig3 := Sig{bytes: [64]byte{3}} data := []Sig{sig1, sig2, sig3} // First two bytes encode the length of the slice.