Skip to content

Commit a132dea

Browse files
committed
proof: add universe commitments record types
1 parent 89891ee commit a132dea

File tree

4 files changed

+504
-0
lines changed

4 files changed

+504
-0
lines changed

proof/encoding.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,76 @@ func PublicKeyOptionDecoder(r io.Reader, val any, buf *[8]byte,
642642
val, "*fn.Option[btcec.PublicKey]", l, l,
643643
)
644644
}
645+
646+
// UniCommitmentVersionEncoder is a function that can be used to encode a
647+
// UniCommitmentVersion to a writer.
648+
func UniCommitmentVersionEncoder(w io.Writer, val any, buf *[8]byte) error {
649+
if version, ok := val.(*UniCommitmentVersion); ok {
650+
versionUint8 := uint8(*version)
651+
return tlv.EUint8(w, &versionUint8, buf)
652+
}
653+
654+
return tlv.NewTypeForEncodingErr(val, "UniCommitmentVersion")
655+
}
656+
657+
// UniCommitmentVersionDecoder is a function that can be used to decode a
658+
// UniCommitmentVersion from a reader.
659+
func UniCommitmentVersionDecoder(r io.Reader, val any, buf *[8]byte,
660+
l uint64) error {
661+
662+
if version, ok := val.(*UniCommitmentVersion); ok {
663+
var versionInt uint8
664+
err := tlv.DUint8(r, &versionInt, buf, l)
665+
if err != nil {
666+
return err
667+
}
668+
669+
*version = UniCommitmentVersion(versionInt)
670+
return nil
671+
}
672+
673+
return tlv.NewTypeForDecodingErr(val, "UniCommitmentVersion", l, 1)
674+
}
675+
676+
// UniCommitmentParamsEncoder is a function that can be used to encode a
677+
// UniCommitmentParams to a writer.
678+
func UniCommitmentParamsEncoder(w io.Writer, val any, buf *[8]byte) error {
679+
if t, ok := val.(*UniCommitmentParams); ok {
680+
var paramsBuf bytes.Buffer
681+
if err := t.Encode(&paramsBuf); err != nil {
682+
return err
683+
}
684+
685+
paramsBytes := paramsBuf.Bytes()
686+
return asset.InlineVarBytesEncoder(w, &paramsBytes, buf)
687+
}
688+
689+
return tlv.NewTypeForEncodingErr(val, "UniCommitmentParams")
690+
}
691+
692+
// UniCommitmentParamsDecoder is a function that can be used to decode a
693+
// UniCommitmentParams from a reader.
694+
func UniCommitmentParamsDecoder(r io.Reader, val any, buf *[8]byte,
695+
l uint64) error {
696+
697+
if typ, ok := val.(*UniCommitmentParams); ok {
698+
var paramsBytes []byte
699+
err := asset.InlineVarBytesDecoder(
700+
r, &paramsBytes, buf, tlv.MaxRecordSize,
701+
)
702+
if err != nil {
703+
return err
704+
}
705+
706+
var params UniCommitmentParams
707+
err = params.Decode(bytes.NewReader(paramsBytes))
708+
if err != nil {
709+
return err
710+
}
711+
712+
*typ = params
713+
return nil
714+
}
715+
716+
return tlv.NewTypeForDecodingErr(val, "UniCommitmentParams", l, 1)
717+
}

proof/records.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
GenesisRevealType tlv.Type = 23
3030
GroupKeyRevealType tlv.Type = 25
3131
AltLeavesType tlv.Type = 27
32+
UniCommitmentsType tlv.Type = 29
3233

3334
TaprootProofOutputIndexType tlv.Type = 0
3435
TaprootProofInternalKeyType tlv.Type = 2
@@ -60,6 +61,7 @@ var KnownProofTypes = fn.NewSet(
6061
ExclusionProofsType, SplitRootProofType, MetaRevealType,
6162
AdditionalInputsType, ChallengeWitnessType, BlockHeightType,
6263
GenesisRevealType, GroupKeyRevealType, AltLeavesType,
64+
UniCommitmentsType,
6365
)
6466

6567
// KnownTaprootProofTypes is a set of all known Taproot proof TLV types. This

proof/uni_commitments.go

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package proof
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"crypto/sha256"
7+
"errors"
8+
"io"
9+
10+
"github.com/btcsuite/btcd/btcec/v2"
11+
"github.com/btcsuite/btcd/wire"
12+
"github.com/lightninglabs/lndclient"
13+
"github.com/lightningnetwork/lnd/keychain"
14+
"github.com/lightningnetwork/lnd/lnwire"
15+
"github.com/lightningnetwork/lnd/tlv"
16+
)
17+
18+
var (
19+
// ErrSignatureVerificationFailed is an error that is returned when the
20+
// signature verification of the UniCommitments fails.
21+
ErrSignatureVerificationFailed = errors.New(
22+
"signature verification failed",
23+
)
24+
)
25+
26+
// UniCommitmentVersion is a type that represents the version of the
27+
// UniCommitments.
28+
type UniCommitmentVersion uint8
29+
30+
const (
31+
// UniCommitmentV0 is the first version of the UniCommitments.
32+
UniCommitmentV0 UniCommitmentVersion = 0
33+
)
34+
35+
// Record returns a TLV record that can be used to encode/decode a
36+
// UniCommitmentVersion to/from a TLV stream.
37+
func (v *UniCommitmentVersion) Record() tlv.Record {
38+
// We set the type to zero here because the type parameter in
39+
// tlv.RecordT will be used as the actual type.
40+
return tlv.MakeStaticRecord(
41+
0, v, 1, UniCommitmentVersionEncoder,
42+
UniCommitmentVersionDecoder,
43+
)
44+
}
45+
46+
// UniCommitmentParams is a struct that holds the parameters for universe
47+
// commitments.
48+
type UniCommitmentParams struct {
49+
Version tlv.RecordT[tlv.TlvType0, UniCommitmentVersion]
50+
PreCommitmentIndex tlv.RecordT[tlv.TlvType2, uint32]
51+
}
52+
53+
// NewUniCommitmentParams creates a new UniCommitmentParams instance with the
54+
// given version and pre-commitment index.
55+
func NewUniCommitmentParams(version UniCommitmentVersion,
56+
preCommitmentIndex uint32) *UniCommitmentParams {
57+
58+
return &UniCommitmentParams{
59+
Version: tlv.NewRecordT[tlv.TlvType0](version),
60+
PreCommitmentIndex: tlv.NewPrimitiveRecord[tlv.TlvType2](
61+
preCommitmentIndex,
62+
),
63+
}
64+
}
65+
66+
// Encode serializes the UniCommitmentParams to the given io.Writer.
67+
func (p *UniCommitmentParams) Encode(w io.Writer) error {
68+
records := []tlv.Record{
69+
p.Version.Record(),
70+
p.PreCommitmentIndex.Record(),
71+
}
72+
73+
// Create the tlv stream.
74+
tlvStream, err := tlv.NewStream(records...)
75+
if err != nil {
76+
return err
77+
}
78+
79+
return tlvStream.Encode(w)
80+
}
81+
82+
// Decode deserializes the UniCommitmentParams from the given io.Reader.
83+
func (p *UniCommitmentParams) Decode(r io.Reader) error {
84+
tlvStream, err := tlv.NewStream(
85+
p.Version.Record(),
86+
p.PreCommitmentIndex.Record(),
87+
)
88+
if err != nil {
89+
return err
90+
}
91+
92+
return tlvStream.DecodeP2P(r)
93+
}
94+
95+
// Bytes returns the serialized UniCommitmentParams record.
96+
func (p *UniCommitmentParams) Bytes() []byte {
97+
var buf bytes.Buffer
98+
_ = p.Encode(&buf)
99+
return buf.Bytes()
100+
}
101+
102+
// Record creates a Record out of a UniCommitmentParams.
103+
//
104+
// NOTE: This is part of the tlv.RecordProducer interface.
105+
func (p *UniCommitmentParams) Record() tlv.Record {
106+
size := func() uint64 {
107+
var (
108+
buf bytes.Buffer
109+
scratch [8]byte
110+
)
111+
err := UniCommitmentParamsEncoder(&buf, p, &scratch)
112+
if err != nil {
113+
panic(err)
114+
}
115+
116+
return uint64(buf.Len())
117+
}
118+
return tlv.MakeDynamicRecord(
119+
0, p, size, UniCommitmentParamsEncoder,
120+
UniCommitmentParamsDecoder,
121+
)
122+
}
123+
124+
// UniCommitments is a struct that holds the universe commitment parameters and
125+
// the authorized signature over those parameters.
126+
type UniCommitments struct {
127+
Params tlv.OptionalRecordT[tlv.TlvType0, UniCommitmentParams]
128+
Sig tlv.RecordT[tlv.TlvType2, lnwire.Sig]
129+
}
130+
131+
// NewUniCommitments creates a new UniCommitments instance with the given
132+
// parameters and signature.
133+
func NewUniCommitments(params *UniCommitmentParams,
134+
sig lnwire.Sig) *UniCommitments {
135+
136+
var paramsRecord tlv.OptionalRecordT[tlv.TlvType0, UniCommitmentParams]
137+
if params != nil {
138+
paramsRecord = tlv.SomeRecordT[tlv.TlvType0](
139+
tlv.NewRecordT[tlv.TlvType0](*params),
140+
)
141+
}
142+
143+
return &UniCommitments{
144+
Params: paramsRecord,
145+
Sig: tlv.NewRecordT[tlv.TlvType2](sig),
146+
}
147+
}
148+
149+
// Encode serializes the UniCommitments to the given io.Writer.
150+
func (p *UniCommitments) Encode(w io.Writer) error {
151+
records := []tlv.Record{
152+
p.Sig.Record(),
153+
}
154+
155+
p.Params.WhenSome(
156+
func(r tlv.RecordT[tlv.TlvType0, UniCommitmentParams]) {
157+
records = append(records, r.Record())
158+
},
159+
)
160+
161+
tlv.SortRecords(records)
162+
163+
// Create the tlv stream.
164+
tlvStream, err := tlv.NewStream(records...)
165+
if err != nil {
166+
return err
167+
}
168+
169+
return tlvStream.Encode(w)
170+
}
171+
172+
// Decode deserializes the UniCommitments from the given io.Reader.
173+
func (p *UniCommitments) Decode(r io.Reader) error {
174+
params := p.Params.Zero()
175+
176+
tlvStream, err := tlv.NewStream(
177+
params.Record(),
178+
p.Sig.Record(),
179+
)
180+
if err != nil {
181+
return err
182+
}
183+
184+
tlvs, err := tlvStream.DecodeWithParsedTypesP2P(r)
185+
if err != nil {
186+
return err
187+
}
188+
189+
if _, ok := tlvs[params.TlvType()]; ok {
190+
p.Params = tlv.SomeRecordT(params)
191+
}
192+
193+
// We need to force the signature type to be a Schnorr signature for the
194+
// unit tests to pass.
195+
var emptySig lnwire.Sig
196+
if p.Sig.Val != emptySig {
197+
p.Sig.Val.ForceSchnorr()
198+
}
199+
200+
return nil
201+
}
202+
203+
// Bytes returns the serialized UniCommitments record.
204+
func (p *UniCommitments) Bytes() []byte {
205+
var buf bytes.Buffer
206+
_ = p.Encode(&buf)
207+
return buf.Bytes()
208+
}
209+
210+
// VerificationDigest returns the digest of the UniCommitments that should be
211+
// used for verification of the signature.
212+
func (p *UniCommitments) VerificationDigest(mintPoint wire.OutPoint) [32]byte {
213+
hash := sha256.New()
214+
_ = wire.WriteOutPoint(hash, 0, 0, &mintPoint)
215+
p.Params.ValOpt().WhenSome(func(params UniCommitmentParams) {
216+
_ = params.Encode(hash)
217+
})
218+
219+
return ([32]byte)(hash.Sum(nil))
220+
}
221+
222+
// Record creates a Record out of a UniCommitments.
223+
//
224+
// NOTE: This is part of the tlv.RecordProducer interface.
225+
func (p *UniCommitments) Record() tlv.Record {
226+
size := func() uint64 {
227+
var (
228+
buf bytes.Buffer
229+
scratch [8]byte
230+
)
231+
err := UniCommitmentParamsEncoder(&buf, p, &scratch)
232+
if err != nil {
233+
panic(err)
234+
}
235+
236+
return uint64(buf.Len())
237+
}
238+
return tlv.MakeDynamicRecord(
239+
0, p, size, UniCommitmentParamsEncoder,
240+
UniCommitmentParamsDecoder,
241+
)
242+
}
243+
244+
// Sign creates a signed commitment over the UniCommitments.
245+
func (p *UniCommitments) Sign(ctx context.Context,
246+
signer lndclient.SignerClient, key keychain.KeyLocator,
247+
mintPoint wire.OutPoint) error {
248+
249+
digest := p.VerificationDigest(mintPoint)
250+
sig, err := signer.SignMessage(
251+
ctx, digest[:], key, lndclient.SignSchnorr(nil),
252+
)
253+
if err != nil {
254+
return err
255+
}
256+
257+
schnorrSig, err := lnwire.NewSigFromSchnorrRawSignature(sig)
258+
if err != nil {
259+
return err
260+
}
261+
262+
p.Sig.Val = schnorrSig
263+
264+
return nil
265+
}
266+
267+
// Verify verifies the signature of the UniCommitments.
268+
func (p *UniCommitments) Verify(mintPoint wire.OutPoint,
269+
key *btcec.PublicKey) error {
270+
271+
digest := p.VerificationDigest(mintPoint)
272+
273+
sig, err := p.Sig.Val.ToSignature()
274+
if err != nil {
275+
return err
276+
}
277+
278+
if !sig.Verify(digest[:], key) {
279+
return ErrSignatureVerificationFailed
280+
}
281+
282+
return nil
283+
}

0 commit comments

Comments
 (0)