Skip to content

proof: add signed universe commitment params #1363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions proof/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,3 +642,76 @@ func PublicKeyOptionDecoder(r io.Reader, val any, buf *[8]byte,
val, "*fn.Option[btcec.PublicKey]", l, l,
)
}

// UniCommitmentVersionEncoder is a function that can be used to encode a
// UniCommitmentVersion to a writer.
func UniCommitmentVersionEncoder(w io.Writer, val any, buf *[8]byte) error {
if version, ok := val.(*UniCommitmentVersion); ok {
versionUint8 := uint8(*version)
return tlv.EUint8(w, &versionUint8, buf)
}

return tlv.NewTypeForEncodingErr(val, "UniCommitmentVersion")
}

// UniCommitmentVersionDecoder is a function that can be used to decode a
// UniCommitmentVersion from a reader.
func UniCommitmentVersionDecoder(r io.Reader, val any, buf *[8]byte,
l uint64) error {

if version, ok := val.(*UniCommitmentVersion); ok {
var versionInt uint8
err := tlv.DUint8(r, &versionInt, buf, l)
if err != nil {
return err
}

*version = UniCommitmentVersion(versionInt)
return nil
}

return tlv.NewTypeForDecodingErr(val, "UniCommitmentVersion", l, 1)
}

// UniCommitmentParamsEncoder is a function that can be used to encode a
// UniCommitmentParams to a writer.
func UniCommitmentParamsEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*UniCommitmentParams); ok {
var paramsBuf bytes.Buffer
if err := t.Encode(&paramsBuf); err != nil {
return err
}

paramsBytes := paramsBuf.Bytes()
return asset.InlineVarBytesEncoder(w, &paramsBytes, buf)
}

return tlv.NewTypeForEncodingErr(val, "UniCommitmentParams")
}

// UniCommitmentParamsDecoder is a function that can be used to decode a
// UniCommitmentParams from a reader.
func UniCommitmentParamsDecoder(r io.Reader, val any, buf *[8]byte,
l uint64) error {

if typ, ok := val.(*UniCommitmentParams); ok {
var paramsBytes []byte
err := asset.InlineVarBytesDecoder(
r, &paramsBytes, buf, tlv.MaxRecordSize,
)
if err != nil {
return err
}

var params UniCommitmentParams
err = params.Decode(bytes.NewReader(paramsBytes))
if err != nil {
return err
}

*typ = params
return nil
}

return tlv.NewTypeForDecodingErr(val, "UniCommitmentParams", l, 1)
}
2 changes: 2 additions & 0 deletions proof/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
GenesisRevealType tlv.Type = 23
GroupKeyRevealType tlv.Type = 25
AltLeavesType tlv.Type = 27
UniCommitmentsType tlv.Type = 29

TaprootProofOutputIndexType tlv.Type = 0
TaprootProofInternalKeyType tlv.Type = 2
Expand Down Expand Up @@ -60,6 +61,7 @@ var KnownProofTypes = fn.NewSet(
ExclusionProofsType, SplitRootProofType, MetaRevealType,
AdditionalInputsType, ChallengeWitnessType, BlockHeightType,
GenesisRevealType, GroupKeyRevealType, AltLeavesType,
UniCommitmentsType,
)

// KnownTaprootProofTypes is a set of all known Taproot proof TLV types. This
Expand Down
283 changes: 283 additions & 0 deletions proof/uni_commitments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package proof

import (
"bytes"
"context"
"crypto/sha256"
"errors"
"io"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tlv"
)

var (
// ErrSignatureVerificationFailed is an error that is returned when the
// signature verification of the UniCommitments fails.
ErrSignatureVerificationFailed = errors.New(
"signature verification failed",
)
)

// UniCommitmentVersion is a type that represents the version of the
// UniCommitments.
type UniCommitmentVersion uint8

const (
// UniCommitmentV0 is the first version of the UniCommitments.
UniCommitmentV0 UniCommitmentVersion = 0
)

// Record returns a TLV record that can be used to encode/decode a
// UniCommitmentVersion to/from a TLV stream.
func (v *UniCommitmentVersion) Record() tlv.Record {
// We set the type to zero here because the type parameter in
// tlv.RecordT will be used as the actual type.
return tlv.MakeStaticRecord(
0, v, 1, UniCommitmentVersionEncoder,
UniCommitmentVersionDecoder,
)
}

// UniCommitmentParams is a struct that holds the parameters for universe
// commitments.
type UniCommitmentParams struct {
Version tlv.RecordT[tlv.TlvType0, UniCommitmentVersion]
PreCommitmentIndex tlv.RecordT[tlv.TlvType2, uint32]
}

// NewUniCommitmentParams creates a new UniCommitmentParams instance with the
// given version and pre-commitment index.
func NewUniCommitmentParams(version UniCommitmentVersion,
preCommitmentIndex uint32) *UniCommitmentParams {

return &UniCommitmentParams{
Version: tlv.NewRecordT[tlv.TlvType0](version),
PreCommitmentIndex: tlv.NewPrimitiveRecord[tlv.TlvType2](
preCommitmentIndex,
),
}
}

// Encode serializes the UniCommitmentParams to the given io.Writer.
func (p *UniCommitmentParams) Encode(w io.Writer) error {
records := []tlv.Record{
p.Version.Record(),
p.PreCommitmentIndex.Record(),
}

// Create the tlv stream.
tlvStream, err := tlv.NewStream(records...)
if err != nil {
return err
}

return tlvStream.Encode(w)
}

// Decode deserializes the UniCommitmentParams from the given io.Reader.
func (p *UniCommitmentParams) Decode(r io.Reader) error {
tlvStream, err := tlv.NewStream(
p.Version.Record(),
p.PreCommitmentIndex.Record(),
)
if err != nil {
return err
}

return tlvStream.DecodeP2P(r)
}

// Bytes returns the serialized UniCommitmentParams record.
func (p *UniCommitmentParams) Bytes() []byte {
var buf bytes.Buffer
_ = p.Encode(&buf)
return buf.Bytes()
}

// Record creates a Record out of a UniCommitmentParams.
//
// NOTE: This is part of the tlv.RecordProducer interface.
func (p *UniCommitmentParams) Record() tlv.Record {
size := func() uint64 {
var (
buf bytes.Buffer
scratch [8]byte
)
err := UniCommitmentParamsEncoder(&buf, p, &scratch)
if err != nil {
panic(err)
}

return uint64(buf.Len())
}
return tlv.MakeDynamicRecord(
0, p, size, UniCommitmentParamsEncoder,
UniCommitmentParamsDecoder,
)
}

// UniCommitments is a struct that holds the universe commitment parameters and
// the authorized signature over those parameters.
type UniCommitments struct {
Params tlv.OptionalRecordT[tlv.TlvType0, UniCommitmentParams]
Sig tlv.RecordT[tlv.TlvType2, lnwire.Sig]
}

// NewUniCommitments creates a new UniCommitments instance with the given
// parameters and signature.
func NewUniCommitments(params *UniCommitmentParams,
sig lnwire.Sig) *UniCommitments {

var paramsRecord tlv.OptionalRecordT[tlv.TlvType0, UniCommitmentParams]
if params != nil {
paramsRecord = tlv.SomeRecordT[tlv.TlvType0](
tlv.NewRecordT[tlv.TlvType0](*params),
)
}

return &UniCommitments{
Params: paramsRecord,
Sig: tlv.NewRecordT[tlv.TlvType2](sig),
}
}

// Encode serializes the UniCommitments to the given io.Writer.
func (p *UniCommitments) Encode(w io.Writer) error {
records := []tlv.Record{
p.Sig.Record(),
}

p.Params.WhenSome(
func(r tlv.RecordT[tlv.TlvType0, UniCommitmentParams]) {
records = append(records, r.Record())
},
)

tlv.SortRecords(records)

// Create the tlv stream.
tlvStream, err := tlv.NewStream(records...)
if err != nil {
return err
}

return tlvStream.Encode(w)
}

// Decode deserializes the UniCommitments from the given io.Reader.
func (p *UniCommitments) Decode(r io.Reader) error {
params := p.Params.Zero()

tlvStream, err := tlv.NewStream(
params.Record(),
p.Sig.Record(),
)
if err != nil {
return err
}

tlvs, err := tlvStream.DecodeWithParsedTypesP2P(r)
if err != nil {
return err
}

if _, ok := tlvs[params.TlvType()]; ok {
p.Params = tlv.SomeRecordT(params)
}

// We need to force the signature type to be a Schnorr signature for the
// unit tests to pass.
var emptySig lnwire.Sig
if p.Sig.Val != emptySig {
p.Sig.Val.ForceSchnorr()
}

return nil
}

// Bytes returns the serialized UniCommitments record.
func (p *UniCommitments) Bytes() []byte {
var buf bytes.Buffer
_ = p.Encode(&buf)
return buf.Bytes()
}

// VerificationDigest returns the digest of the UniCommitments that should be
// used for verification of the signature.
func (p *UniCommitments) VerificationDigest(mintPoint wire.OutPoint) [32]byte {
hash := sha256.New()
_ = wire.WriteOutPoint(hash, 0, 0, &mintPoint)
p.Params.ValOpt().WhenSome(func(params UniCommitmentParams) {
_ = params.Encode(hash)
})

return ([32]byte)(hash.Sum(nil))
}

// Record creates a Record out of a UniCommitments.
//
// NOTE: This is part of the tlv.RecordProducer interface.
func (p *UniCommitments) Record() tlv.Record {
size := func() uint64 {
var (
buf bytes.Buffer
scratch [8]byte
)
err := UniCommitmentParamsEncoder(&buf, p, &scratch)
if err != nil {
panic(err)
}

return uint64(buf.Len())
}
return tlv.MakeDynamicRecord(
0, p, size, UniCommitmentParamsEncoder,
UniCommitmentParamsDecoder,
)
}

// Sign creates a signed commitment over the UniCommitments.
func (p *UniCommitments) Sign(ctx context.Context,
signer lndclient.SignerClient, key keychain.KeyLocator,
mintPoint wire.OutPoint) error {

digest := p.VerificationDigest(mintPoint)
sig, err := signer.SignMessage(
ctx, digest[:], key, lndclient.SignSchnorr(nil),
)
if err != nil {
return err
}

schnorrSig, err := lnwire.NewSigFromSchnorrRawSignature(sig)
if err != nil {
return err
}

p.Sig.Val = schnorrSig

return nil
}

// Verify verifies the signature of the UniCommitments.
func (p *UniCommitments) Verify(mintPoint wire.OutPoint,
key *btcec.PublicKey) error {

digest := p.VerificationDigest(mintPoint)

sig, err := p.Sig.Val.ToSignature()
if err != nil {
return err
}

if !sig.Verify(digest[:], key) {
return ErrSignatureVerificationFailed
}

return nil
}
Loading
Loading