Skip to content

Commit 964f42c

Browse files
committed
loadtest: add mintTestV2
We add a new mintV2 test which mints normal assets of a configured supply into a fixed number of groups. This is an enhanced and more lightweight version of the previous mint test, as it uses less assertions and rpc calls.
1 parent 7f26e1b commit 964f42c

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

itest/loadtest/load_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ var loadTestCases = []testCase{
3939
name: "mint",
4040
fn: mintTest,
4141
},
42+
{
43+
name: "mintV2",
44+
fn: mintTestV2,
45+
},
4246
{
4347
name: "send",
4448
fn: sendTest,

itest/loadtest/mint_batch_test.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ import (
99
"math/rand"
1010
"strings"
1111
"testing"
12+
"time"
1213

14+
"github.com/btcsuite/btcd/rpcclient"
1315
"github.com/lightninglabs/taproot-assets/fn"
1416
"github.com/lightninglabs/taproot-assets/itest"
1517
"github.com/lightninglabs/taproot-assets/taprpc"
1618
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
1719
unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc"
20+
"github.com/lightningnetwork/lnd/lntest/wait"
1821
"github.com/stretchr/testify/require"
1922
)
2023

@@ -166,3 +169,198 @@ func mintTest(t *testing.T, ctx context.Context, cfg *Config) {
166169

167170
itest.SyncUniverses(ctx, t, bob, alice, aliceHost, cfg.TestTimeout)
168171
}
172+
173+
// mintTestV2 checks that we can mint a batch of assets. It is a more
174+
// performant version of the existing mintTest, as it uses less assertions and
175+
// RPC calls.
176+
func mintTestV2(t *testing.T, ctx context.Context, cfg *Config) {
177+
// Start by initializing all our client connections.
178+
alice, bob, bitcoinClient := initClients(t, ctx, cfg)
179+
180+
// We query the assets of each node once on this step. Every function
181+
// that needs to take a node's assets into account will be passed these
182+
// values instead of calling the RPC again. This is done to minimize
183+
// collateral RPC impact of the loadtest.
184+
resAlice, err := alice.ListAssets(ctx, &taprpc.ListAssetRequest{})
185+
require.NoError(t, err)
186+
187+
resBob, err := bob.ListAssets(ctx, &taprpc.ListAssetRequest{})
188+
require.NoError(t, err)
189+
190+
assetsAlice := resAlice.Assets
191+
assetsBob := resBob.Assets
192+
193+
totalAssets := make([]*taprpc.Asset, len(assetsAlice)+len(assetsBob))
194+
copy(totalAssets, assetsAlice)
195+
copy(totalAssets[len(assetsAlice):], assetsBob)
196+
197+
// Alice serves as the minter.
198+
minter := alice
199+
200+
// First we make sure group initialization is completed. We check if
201+
// there's any more groups left
202+
existingGroups := getTotalAssetGroups(totalAssets)
203+
groupKeys := make(map[string][]byte, 0)
204+
205+
for _, v := range existingGroups {
206+
tweakedKey, err := hex.DecodeString(v)
207+
require.NoError(t, err)
208+
209+
groupKeys[v] = tweakedKey
210+
}
211+
212+
var remainingGroups int
213+
if cfg.TotalNumGroups > len(existingGroups) {
214+
remainingGroups = cfg.TotalNumGroups - len(existingGroups)
215+
}
216+
217+
t.Logf("Existing groups=%v, minting %v new groups",
218+
len(existingGroups), remainingGroups)
219+
for range remainingGroups {
220+
mintNewGroup(t, ctx, bitcoinClient, minter, cfg)
221+
}
222+
223+
// If there's not any existing groups we skip the rest of the steps, we
224+
// will mint into those groups in another run.
225+
if len(existingGroups) == 0 {
226+
return
227+
}
228+
229+
groupIndex := rand.Intn(len(existingGroups))
230+
groupKey := groupKeys[existingGroups[groupIndex]]
231+
232+
mintIntoGroup(t, ctx, bitcoinClient, minter, groupKey, cfg)
233+
}
234+
235+
// mintNewGroup mints an asset that creates a new group.
236+
func mintNewGroup(t *testing.T, ctx context.Context, miner *rpcclient.Client,
237+
minter *rpcClient, cfg *Config) []*taprpc.Asset {
238+
239+
mintAmt := rand.Uint64() % uint64(cfg.MintSupplyMax)
240+
if mintAmt < uint64(cfg.MintSupplyMin) {
241+
mintAmt = uint64(cfg.MintSupplyMin)
242+
}
243+
244+
// nolint:lll
245+
assetRequests := []*mintrpc.MintAssetRequest{
246+
{
247+
Asset: &mintrpc.MintAsset{
248+
AssetType: taprpc.AssetType_NORMAL,
249+
Name: fmt.Sprintf(
250+
"tapcoin-%d", time.Now().UnixNano(),
251+
),
252+
AssetMeta: &taprpc.AssetMeta{
253+
Data: []byte("{}"),
254+
Type: taprpc.AssetMetaType_META_TYPE_JSON,
255+
},
256+
Amount: mintAmt,
257+
NewGroupedAsset: true,
258+
DecimalDisplay: 4,
259+
},
260+
},
261+
}
262+
263+
return finishMint(t, ctx, miner, minter, assetRequests)
264+
}
265+
266+
// mintIntoGroup mints as many assets as the batch size and puts them in the
267+
// existing group that is provided by the corresponding argument.
268+
func mintIntoGroup(t *testing.T, ctx context.Context, miner *rpcclient.Client,
269+
minter *rpcClient, tweakedKey []byte, cfg *Config) []*taprpc.Asset {
270+
271+
mintAmt := rand.Uint64() % uint64(cfg.MintSupplyMax)
272+
if mintAmt < uint64(cfg.MintSupplyMin) {
273+
mintAmt = uint64(cfg.MintSupplyMin)
274+
}
275+
276+
var assetRequests []*mintrpc.MintAssetRequest
277+
278+
t.Logf("Minting %v assets into group %s", cfg.BatchSize, tweakedKey)
279+
280+
for range cfg.BatchSize {
281+
ts := time.Now().UnixNano()
282+
283+
// nolint:lll
284+
req := &mintrpc.MintAssetRequest{
285+
Asset: &mintrpc.MintAsset{
286+
AssetType: taprpc.AssetType_NORMAL,
287+
Name: fmt.Sprintf("tapcoin-%d", ts),
288+
AssetMeta: &taprpc.AssetMeta{
289+
Data: []byte("{}"),
290+
Type: taprpc.AssetMetaType_META_TYPE_JSON,
291+
},
292+
Amount: mintAmt,
293+
GroupedAsset: true,
294+
GroupKey: tweakedKey,
295+
DecimalDisplay: 4,
296+
},
297+
}
298+
299+
assetRequests = append(assetRequests, req)
300+
}
301+
302+
return finishMint(t, ctx, miner, minter, assetRequests)
303+
}
304+
305+
// finishMint accepts a list of asset requests and performs the necessary RPC
306+
// calls to create and finalize a minting batch.
307+
func finishMint(t *testing.T, ctx context.Context, miner *rpcclient.Client,
308+
minter *rpcClient,
309+
assetRequests []*mintrpc.MintAssetRequest) []*taprpc.Asset {
310+
311+
ctxc, streamCancel := context.WithCancel(ctx)
312+
stream, err := minter.SubscribeMintEvents(
313+
ctxc, &mintrpc.SubscribeMintEventsRequest{},
314+
)
315+
require.NoError(t, err)
316+
sub := &itest.EventSubscription[*mintrpc.MintEvent]{
317+
ClientEventStream: stream,
318+
Cancel: streamCancel,
319+
}
320+
321+
itest.BuildMintingBatch(t, minter, assetRequests)
322+
323+
ctxb := context.Background()
324+
ctxt, cancel := context.WithTimeout(ctxb, wait.DefaultTimeout)
325+
defer cancel()
326+
327+
finalizeReq := &mintrpc.FinalizeBatchRequest{}
328+
// Instruct the daemon to finalize the batch.
329+
batchResp, err := minter.FinalizeBatch(ctxt, finalizeReq)
330+
require.NoError(t, err)
331+
require.NotEmpty(t, batchResp.Batch)
332+
require.Len(t, batchResp.Batch.Assets, len(assetRequests))
333+
require.Equal(
334+
t, mintrpc.BatchState_BATCH_STATE_BROADCAST,
335+
batchResp.Batch.State,
336+
)
337+
338+
itest.WaitForBatchState(
339+
t, ctxt, minter, wait.DefaultTimeout,
340+
batchResp.Batch.BatchKey,
341+
mintrpc.BatchState_BATCH_STATE_BROADCAST,
342+
)
343+
hashes, err := itest.WaitForNTxsInMempool(
344+
miner, 1, wait.DefaultTimeout,
345+
)
346+
require.NoError(t, err)
347+
require.GreaterOrEqual(t, len(hashes), 1)
348+
349+
return itest.ConfirmBatch(
350+
t, miner, minter, assetRequests, sub, *hashes[0],
351+
batchResp.Batch.BatchKey,
352+
)
353+
}
354+
355+
// getTotalAssetGroups returns the total number of asset groups found in the
356+
// passed array of assets.
357+
func getTotalAssetGroups(assets []*taprpc.Asset) []string {
358+
groups := fn.NewSet[string]()
359+
360+
for _, v := range assets {
361+
groupKeyStr := fmt.Sprintf("%x", v.AssetGroup.TweakedGroupKey)
362+
groups.Add(groupKeyStr)
363+
}
364+
365+
return groups.ToSlice()
366+
}

0 commit comments

Comments
 (0)