@@ -21,6 +21,7 @@ import (
21
21
"github.com/btcsuite/btcwallet/waddrmgr"
22
22
"github.com/lightninglabs/lndclient"
23
23
"github.com/lightninglabs/taproot-assets/asset"
24
+ "github.com/lightninglabs/taproot-assets/fn"
24
25
"github.com/lightninglabs/taproot-assets/internal/test"
25
26
"github.com/lightninglabs/taproot-assets/proof"
26
27
"github.com/lightninglabs/taproot-assets/tapscript"
@@ -56,12 +57,110 @@ func RandSeedlings(t testing.TB, numSeedlings int) map[string]*Seedling {
56
57
return seedlings
57
58
}
58
59
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
+
59
149
// MintBatchOptions is a set of options for creating a new minting batch.
60
150
type MintBatchOptions struct {
61
151
// totalSeedlings specifies the number of seedlings to generate in this
62
152
// minting batch. The seedlings are randomly assigned as grouped or
63
153
// ungrouped.
64
154
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
65
164
}
66
165
67
166
// MintBatchOption is a functional option for creating a new minting batch.
@@ -80,6 +179,23 @@ func WithTotalSeedlings(count int) MintBatchOption {
80
179
}
81
180
}
82
181
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
+
83
199
// RandMintingBatch creates a new minting batch with only random seedlings
84
200
// populated for testing.
85
201
func RandMintingBatch (t testing.TB , opts ... MintBatchOption ) * MintingBatch {
@@ -89,12 +205,49 @@ func RandMintingBatch(t testing.TB, opts ...MintBatchOption) *MintingBatch {
89
205
opt (& options )
90
206
}
91
207
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
+
92
244
batchKey , _ := test .RandKeyDesc (t )
93
245
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 ,
98
251
}
99
252
100
253
walletFundPsbt := func (ctx context.Context ,
0 commit comments