@@ -21,6 +21,7 @@ import (
2121 "github.com/btcsuite/btcwallet/waddrmgr"
2222 "github.com/lightninglabs/lndclient"
2323 "github.com/lightninglabs/taproot-assets/asset"
24+ "github.com/lightninglabs/taproot-assets/fn"
2425 "github.com/lightninglabs/taproot-assets/internal/test"
2526 "github.com/lightninglabs/taproot-assets/proof"
2627 "github.com/lightninglabs/taproot-assets/tapscript"
@@ -56,12 +57,110 @@ func RandSeedlings(t testing.TB, numSeedlings int) map[string]*Seedling {
5657 return seedlings
5758}
5859
60+ // RandGroupSeedlings generates a random set of seedlings for a single asset
61+ // group.
62+ func RandGroupSeedlings (t testing.TB , numSeedlings int ,
63+ uniCommitments bool ) map [string ]* Seedling {
64+
65+ seedlings := make (map [string ]* Seedling )
66+
67+ // Formulate group anchor seedling.
68+ metaBlob := test .RandBytes (32 )
69+ groupAnchorName := hex .EncodeToString (test .RandBytes (32 ))
70+ assetType := asset .Normal
71+
72+ // For now, we only test the v0 and v1 versions.
73+ assetVersion := asset .Version (test .RandIntn (2 ))
74+
75+ scriptKey , _ := test .RandKeyDesc (t )
76+
77+ // If universe commitments are enabled, we generate a random key
78+ // descriptor to use as the delegation key.
79+ var delegationKey fn.Option [keychain.KeyDescriptor ]
80+ if uniCommitments {
81+ keyDesc , _ := test .RandKeyDesc (t )
82+ delegationKey = fn .Some (keyDesc )
83+ }
84+
85+ assetGenesis := asset .RandGenesis (t , assetType )
86+
87+ // Create asset group key.
88+ groupPrivateDesc , groupPrivateKey := test .RandKeyDesc (t )
89+
90+ // Generate the signature for our group genesis asset.
91+ genSigner := asset .NewMockGenesisSigner (groupPrivateKey )
92+ genTxBuilder := asset.MockGroupTxBuilder {}
93+
94+ genProtoAsset := asset .RandAssetWithValues (
95+ t , assetGenesis , nil , asset .RandScriptKey (t ),
96+ )
97+ groupKeyRequest := asset .NewGroupKeyRequestNoErr (
98+ t , groupPrivateDesc , fn .None [asset.ExternalKey ](), assetGenesis ,
99+ genProtoAsset , nil , fn .None [chainhash.Hash ](),
100+ )
101+ genTx , err := groupKeyRequest .BuildGroupVirtualTx (& genTxBuilder )
102+ require .NoError (t , err )
103+
104+ groupKey , err := asset .DeriveGroupKey (
105+ genSigner , * genTx , * groupKeyRequest , nil ,
106+ )
107+ require .NoError (t , err )
108+
109+ seedlings [groupAnchorName ] = & Seedling {
110+ AssetVersion : assetVersion ,
111+ AssetType : assetType ,
112+ AssetName : groupAnchorName ,
113+ Meta : & proof.MetaReveal {
114+ Data : metaBlob ,
115+ },
116+ Amount : uint64 (test .RandInt [uint32 ]()),
117+ GroupInfo : & asset.AssetGroup {
118+ Genesis : & assetGenesis ,
119+ GroupKey : groupKey ,
120+ },
121+ ScriptKey : asset .NewScriptKeyBip86 (scriptKey ),
122+ EnableEmission : true ,
123+ UniverseCommitments : uniCommitments ,
124+ DelegationKey : delegationKey ,
125+ }
126+
127+ // Formulate non-anchor group seedlings.
128+ for i := 0 ; i < numSeedlings - 1 ; i ++ {
129+ seedlingName := hex .EncodeToString (test .RandBytes (32 ))
130+
131+ seedlings [groupAnchorName ] = & Seedling {
132+ AssetVersion : assetVersion ,
133+ AssetType : assetType ,
134+ AssetName : seedlingName ,
135+ GroupAnchor : & groupAnchorName ,
136+ Meta : & proof.MetaReveal {
137+ Data : metaBlob ,
138+ },
139+ Amount : uint64 (test .RandInt [uint32 ]()),
140+ ScriptKey : asset .NewScriptKeyBip86 (scriptKey ),
141+ EnableEmission : true ,
142+ UniverseCommitments : uniCommitments ,
143+ }
144+ }
145+
146+ return seedlings
147+ }
148+
59149// MintBatchOptions is a set of options for creating a new minting batch.
60150type MintBatchOptions struct {
61151 // totalSeedlings specifies the number of seedlings to generate in this
62152 // minting batch. The seedlings are randomly assigned as grouped or
63153 // ungrouped.
64154 totalSeedlings int
155+
156+ // totalGroups specifies the number of asset groups to generate in this
157+ // minting batch. Each element in the slice specifies the number of
158+ // seedlings to generate for the corresponding asset group.
159+ totalGroups []int
160+
161+ // universeCommitments specifies whether to generate universe
162+ // commitments for the asset groups in this minting batch.
163+ universeCommitments bool
65164}
66165
67166// MintBatchOption is a functional option for creating a new minting batch.
@@ -80,6 +179,23 @@ func WithTotalSeedlings(count int) MintBatchOption {
80179 }
81180}
82181
182+ // WithTotalGroups sets the total number of asset groups to populate in the
183+ // minting batch. Each element in the slice specifies the number of seedlings
184+ // to generate for the corresponding asset group.
185+ func WithTotalGroups (counts []int ) MintBatchOption {
186+ return func (options * MintBatchOptions ) {
187+ options .totalGroups = counts
188+ }
189+ }
190+
191+ // WithUniverseCommitments specifies whether to generate universe commitments
192+ // for the asset groups in the minting batch.
193+ func WithUniverseCommitments (enabled bool ) MintBatchOption {
194+ return func (options * MintBatchOptions ) {
195+ options .universeCommitments = enabled
196+ }
197+ }
198+
83199// RandMintingBatch creates a new minting batch with only random seedlings
84200// populated for testing.
85201func RandMintingBatch (t testing.TB , opts ... MintBatchOption ) * MintingBatch {
@@ -89,12 +205,49 @@ func RandMintingBatch(t testing.TB, opts ...MintBatchOption) *MintingBatch {
89205 opt (& options )
90206 }
91207
208+ // Formulate batch seedlings.
209+ seedlings := make (map [string ]* Seedling )
210+
211+ // Generate seedlings for each asset group.
212+ for idx := range options .totalGroups {
213+ countSeedlingsInGroup := options .totalGroups [idx ]
214+
215+ groupSeedlings := RandGroupSeedlings (
216+ t , countSeedlingsInGroup , options .universeCommitments ,
217+ )
218+
219+ // Add the seedlings to the total seedlings map.
220+ for name , seedling := range groupSeedlings {
221+ seedlings [name ] = seedling
222+ }
223+ }
224+
225+ // If the total number of seedlings generated so far is less than the
226+ // total number of seedlings requested, we generate the remaining
227+ // seedlings at random.
228+ if len (seedlings ) < options .totalSeedlings {
229+ remaining := options .totalSeedlings - len (seedlings )
230+ randSeedlings := RandSeedlings (t , remaining )
231+
232+ // Add the seedlings to the total seedlings map.
233+ for name , seedling := range randSeedlings {
234+ seedlings [name ] = seedling
235+ }
236+ }
237+
238+ // Randomly generating seedlings may result in overlaps with existing
239+ // ones, leading to fewer seedlings than intended. Sanity check to
240+ // ensure that the total number of seedlings generated matches the
241+ // requested amount. This check might help debug flakes in tests.
242+ require .Equal (t , options .totalSeedlings , len (seedlings ))
243+
92244 batchKey , _ := test .RandKeyDesc (t )
93245 batch := MintingBatch {
94- BatchKey : batchKey ,
95- Seedlings : RandSeedlings (t , options .totalSeedlings ),
96- HeightHint : test .RandInt [uint32 ](),
97- CreationTime : time .Now (),
246+ BatchKey : batchKey ,
247+ Seedlings : seedlings ,
248+ HeightHint : test .RandInt [uint32 ](),
249+ CreationTime : time .Now (),
250+ UniverseCommitments : options .universeCommitments ,
98251 }
99252
100253 walletFundPsbt := func (ctx context.Context ,
0 commit comments