88 "fmt"
99 "io"
1010 "math"
11+ "net/url"
1112
13+ "github.com/btcsuite/btcd/btcec/v2"
1214 "github.com/lightninglabs/taproot-assets/asset"
1315 "github.com/lightninglabs/taproot-assets/fn"
1416 "github.com/lightningnetwork/lnd/tlv"
@@ -46,6 +48,14 @@ const (
4648 // define when minting assets. Since the uint64 max value has 19 decimal
4749 // places we will allow for a max of 12 decimal places.
4850 MaxDecDisplay = uint32 (12 )
51+
52+ // MaxNumCanonicalUniverseURLs is the maximum number of canonical
53+ // universe URLs that can be set.
54+ MaxNumCanonicalUniverseURLs = 16
55+
56+ // MaxCanonicalUniverseURLLength is the maximum length of the canonical
57+ // universe URL.
58+ MaxCanonicalUniverseURLLength = 255
4959)
5060
5161var (
8696 // ErrDecDisplayMissing is returned if the decimal display key is
8797 // not present in a JSON object.
8898 ErrDecDisplayMissing = errors .New ("decimal display field missing" )
99+
100+ // ErrCanonicalUniverseInvalid is returned if the canonical universe
101+ // URL is invalid.
102+ ErrCanonicalUniverseInvalid = errors .New (
103+ "canonical universe URL invalid" ,
104+ )
105+
106+ // ErrTooManyCanonicalUniverseURLs is returned if the number of
107+ // canonical universe URLs exceeds the maximum.
108+ ErrTooManyCanonicalUniverseURLs = fmt .Errorf (
109+ "too many canonical universe URLs, max %d" ,
110+ MaxNumCanonicalUniverseURLs ,
111+ )
112+
113+ // ErrCanonicalUniverseURLTooLong is returned if the canonical universe
114+ // URL is too long.
115+ ErrCanonicalUniverseURLTooLong = fmt .Errorf (
116+ "canonical universe URL too long, max %d characters" ,
117+ MaxCanonicalUniverseURLLength ,
118+ )
119+
120+ // ErrDelegationKeyEmpty is returned if the delegation key is empty.
121+ ErrDelegationKeyEmpty = errors .New ("delegation key is empty" )
122+
123+ // ErrDelegationKeyNotOnCurve is returned if the delegation key is not
124+ // on the curve.
125+ ErrDelegationKeyNotOnCurve = errors .New (
126+ "delegation key is not on curve" ,
127+ )
89128)
90129
91130// MetaReveal is an optional TLV type that can be added to the proof of a
@@ -110,6 +149,22 @@ type MetaReveal struct {
110149 // field to the JSON object for backward compatibility.
111150 DecimalDisplay fn.Option [uint32 ]
112151
152+ // UniverseCommitments indicates that the asset group this asset belongs
153+ // to will create and push universe commitments to the canonical
154+ // universe. A universe commitment is a "proof of inventory" that
155+ // commits the issuer's current total asset balance (sum of all mints
156+ // minus burns or ignored assets) on-chain.
157+ UniverseCommitments bool
158+
159+ // CanonicalUniverses is a list of URLs of the canonical (approved,
160+ // authoritative) universe where the asset minting and universe
161+ // commitment proofs will be pushed to.
162+ CanonicalUniverses fn.Option [[]url.URL ]
163+
164+ // DelegationKey is the public key that is used to verify universe
165+ // commitment related on-chain outputs and proofs.
166+ DelegationKey fn.Option [btcec.PublicKey ]
167+
113168 // UnknownOddTypes is a map of unknown odd types that were encountered
114169 // during decoding. This map is used to preserve unknown types that we
115170 // don't know of yet, so we can still encode them back when serializing.
@@ -152,7 +207,44 @@ func (m *MetaReveal) Validate() error {
152207 }
153208
154209 // If the decimal display is set, it must be valid.
155- return fn .MapOptionZ (m .DecimalDisplay , IsValidDecDisplay )
210+ err = fn .MapOptionZ (m .DecimalDisplay , IsValidDecDisplay )
211+ if err != nil {
212+ return err
213+ }
214+
215+ err = fn .MapOptionZ (m .CanonicalUniverses , func (urls []url.URL ) error {
216+ // If the option is set, the slice must not be empty.
217+ if len (urls ) == 0 {
218+ return ErrCanonicalUniverseInvalid
219+ }
220+
221+ if len (urls ) > MaxNumCanonicalUniverseURLs {
222+ return ErrTooManyCanonicalUniverseURLs
223+ }
224+
225+ for _ , u := range urls {
226+ if len (u .String ()) > MaxCanonicalUniverseURLLength {
227+ return ErrCanonicalUniverseURLTooLong
228+ }
229+ }
230+
231+ return nil
232+ })
233+ if err != nil {
234+ return err
235+ }
236+
237+ return fn .MapOptionZ (m .DelegationKey , func (key btcec.PublicKey ) error {
238+ if key == emptyKey {
239+ return ErrDelegationKeyEmpty
240+ }
241+
242+ if ! key .IsOnCurve () {
243+ return ErrDelegationKeyNotOnCurve
244+ }
245+
246+ return nil
247+ })
156248}
157249
158250// IsValidMetaType checks if the passed value is a valid meta type.
@@ -385,6 +477,15 @@ func (m *MetaReveal) EncodeRecords() []tlv.Record {
385477 MetaRevealDataRecord (& m .Data ),
386478 }
387479
480+ // In order not to change the encoding of existing records if
481+ // we de-serialize and re-serialize them, we only encode this boolean
482+ // value if it's actually true.
483+ if m .UniverseCommitments {
484+ records = append (records , MetaRevealUniverseCommitmentsRecord (
485+ & m .UniverseCommitments ,
486+ ))
487+ }
488+
388489 // To make sure we don't re-encode old assets that don't have a decimal
389490 // display value as a TLV field with a different value, we only encode
390491 // the decimal display value if it is explicitly set.
@@ -394,6 +495,18 @@ func (m *MetaReveal) EncodeRecords() []tlv.Record {
394495 ))
395496 }
396497
498+ if m .CanonicalUniverses .IsSome () {
499+ records = append (records , MetaRevealCanonicalUniversesRecord (
500+ & m .CanonicalUniverses ,
501+ ))
502+ }
503+
504+ if m .DelegationKey .IsSome () {
505+ records = append (records , MetaRevealDelegationKeyRecord (
506+ & m .DelegationKey ,
507+ ))
508+ }
509+
397510 // Add any unknown odd types that were encountered during decoding.
398511 return asset .CombineRecords (records , m .UnknownOddTypes )
399512}
@@ -404,6 +517,9 @@ func (m *MetaReveal) DecodeRecords() []tlv.Record {
404517 MetaRevealTypeRecord (& m .Type ),
405518 MetaRevealDataRecord (& m .Data ),
406519 MetaRevealDecimalDisplayRecord (& m .DecimalDisplay ),
520+ MetaRevealUniverseCommitmentsRecord (& m .UniverseCommitments ),
521+ MetaRevealCanonicalUniversesRecord (& m .CanonicalUniverses ),
522+ MetaRevealDelegationKeyRecord (& m .DelegationKey ),
407523 }
408524}
409525
0 commit comments