-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add mintV2
loadtest
#1285
base: main
Are you sure you want to change the base?
Add mintV2
loadtest
#1285
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,12 +9,15 @@ import ( | |
"math/rand" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/btcsuite/btcd/rpcclient" | ||
"github.com/lightninglabs/taproot-assets/fn" | ||
"github.com/lightninglabs/taproot-assets/itest" | ||
"github.com/lightninglabs/taproot-assets/taprpc" | ||
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc" | ||
unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc" | ||
"github.com/lightningnetwork/lnd/lntest/wait" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
|
@@ -166,3 +169,198 @@ func mintTest(t *testing.T, ctx context.Context, cfg *Config) { | |
|
||
itest.SyncUniverses(ctx, t, bob, alice, aliceHost, cfg.TestTimeout) | ||
} | ||
|
||
// mintTestV2 checks that we can mint a batch of assets. It is a more | ||
// performant version of the existing mintTest, as it uses less assertions and | ||
// RPC calls. | ||
func mintTestV2(t *testing.T, ctx context.Context, cfg *Config) { | ||
// Start by initializing all our client connections. | ||
alice, bob, bitcoinClient := initClients(t, ctx, cfg) | ||
|
||
// We query the assets of each node once on this step. Every function | ||
// that needs to take a node's assets into account will be passed these | ||
// values instead of calling the RPC again. This is done to minimize | ||
// collateral RPC impact of the loadtest. | ||
resAlice, err := alice.ListAssets(ctx, &taprpc.ListAssetRequest{}) | ||
require.NoError(t, err) | ||
|
||
resBob, err := bob.ListAssets(ctx, &taprpc.ListAssetRequest{}) | ||
require.NoError(t, err) | ||
|
||
assetsAlice := resAlice.Assets | ||
assetsBob := resBob.Assets | ||
|
||
totalAssets := make([]*taprpc.Asset, len(assetsAlice)+len(assetsBob)) | ||
copy(totalAssets, assetsAlice) | ||
copy(totalAssets[len(assetsAlice):], assetsBob) | ||
|
||
// Alice serves as the minter. | ||
minter := alice | ||
|
||
// First we make sure group initialization is completed. We check if | ||
// there's any more groups left | ||
existingGroups := getTotalAssetGroups(totalAssets) | ||
groupKeys := make(map[string][]byte, 0) | ||
|
||
for _, v := range existingGroups { | ||
tweakedKey, err := hex.DecodeString(v) | ||
require.NoError(t, err) | ||
|
||
groupKeys[v] = tweakedKey | ||
} | ||
|
||
var remainingGroups int | ||
if cfg.TotalNumGroups > len(existingGroups) { | ||
remainingGroups = cfg.TotalNumGroups - len(existingGroups) | ||
} | ||
|
||
t.Logf("Existing groups=%v, minting %v new groups", | ||
len(existingGroups), remainingGroups) | ||
for range remainingGroups { | ||
mintNewGroup(t, ctx, bitcoinClient, minter, cfg) | ||
} | ||
|
||
// If there's not any existing groups we skip the rest of the steps, we | ||
// will mint into those groups in another run. | ||
if len(existingGroups) == 0 { | ||
return | ||
} | ||
|
||
groupIndex := rand.Intn(len(existingGroups)) | ||
groupKey := groupKeys[existingGroups[groupIndex]] | ||
|
||
mintIntoGroup(t, ctx, bitcoinClient, minter, groupKey, cfg) | ||
} | ||
|
||
// mintNewGroup mints an asset that creates a new group. | ||
func mintNewGroup(t *testing.T, ctx context.Context, miner *rpcclient.Client, | ||
minter *rpcClient, cfg *Config) []*taprpc.Asset { | ||
|
||
mintAmt := rand.Uint64() % uint64(cfg.MintSupplyMax) | ||
if mintAmt < uint64(cfg.MintSupplyMin) { | ||
mintAmt = uint64(cfg.MintSupplyMin) | ||
} | ||
|
||
// nolint:lll | ||
assetRequests := []*mintrpc.MintAssetRequest{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can remove a level of indentation by formatting as:
That way we don't need the |
||
{ | ||
Asset: &mintrpc.MintAsset{ | ||
AssetType: taprpc.AssetType_NORMAL, | ||
Name: fmt.Sprintf( | ||
"tapcoin-%d", time.Now().UnixNano(), | ||
), | ||
AssetMeta: &taprpc.AssetMeta{ | ||
Data: []byte("{}"), | ||
Type: taprpc.AssetMetaType_META_TYPE_JSON, | ||
}, | ||
Amount: mintAmt, | ||
NewGroupedAsset: true, | ||
DecimalDisplay: 4, | ||
}, | ||
}, | ||
} | ||
|
||
return finishMint(t, ctx, miner, minter, assetRequests) | ||
} | ||
|
||
// mintIntoGroup mints as many assets as the batch size and puts them in the | ||
// existing group that is provided by the corresponding argument. | ||
func mintIntoGroup(t *testing.T, ctx context.Context, miner *rpcclient.Client, | ||
minter *rpcClient, tweakedKey []byte, cfg *Config) []*taprpc.Asset { | ||
|
||
mintAmt := rand.Uint64() % uint64(cfg.MintSupplyMax) | ||
if mintAmt < uint64(cfg.MintSupplyMin) { | ||
mintAmt = uint64(cfg.MintSupplyMin) | ||
} | ||
|
||
var assetRequests []*mintrpc.MintAssetRequest | ||
|
||
t.Logf("Minting %v assets into group %s", cfg.BatchSize, tweakedKey) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
|
||
for range cfg.BatchSize { | ||
ts := time.Now().UnixNano() | ||
|
||
// nolint:lll | ||
req := &mintrpc.MintAssetRequest{ | ||
Asset: &mintrpc.MintAsset{ | ||
AssetType: taprpc.AssetType_NORMAL, | ||
Name: fmt.Sprintf("tapcoin-%d", ts), | ||
AssetMeta: &taprpc.AssetMeta{ | ||
Data: []byte("{}"), | ||
Type: taprpc.AssetMetaType_META_TYPE_JSON, | ||
}, | ||
Amount: mintAmt, | ||
GroupedAsset: true, | ||
GroupKey: tweakedKey, | ||
DecimalDisplay: 4, | ||
}, | ||
} | ||
|
||
assetRequests = append(assetRequests, req) | ||
} | ||
|
||
return finishMint(t, ctx, miner, minter, assetRequests) | ||
} | ||
|
||
// finishMint accepts a list of asset requests and performs the necessary RPC | ||
// calls to create and finalize a minting batch. | ||
func finishMint(t *testing.T, ctx context.Context, miner *rpcclient.Client, | ||
minter *rpcClient, | ||
assetRequests []*mintrpc.MintAssetRequest) []*taprpc.Asset { | ||
|
||
ctxc, streamCancel := context.WithCancel(ctx) | ||
stream, err := minter.SubscribeMintEvents( | ||
ctxc, &mintrpc.SubscribeMintEventsRequest{}, | ||
) | ||
require.NoError(t, err) | ||
sub := &itest.EventSubscription[*mintrpc.MintEvent]{ | ||
ClientEventStream: stream, | ||
Cancel: streamCancel, | ||
} | ||
|
||
itest.BuildMintingBatch(t, minter, assetRequests) | ||
|
||
ctxb := context.Background() | ||
ctxt, cancel := context.WithTimeout(ctxb, wait.DefaultTimeout) | ||
defer cancel() | ||
|
||
finalizeReq := &mintrpc.FinalizeBatchRequest{} | ||
// Instruct the daemon to finalize the batch. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: newline before comment block. |
||
batchResp, err := minter.FinalizeBatch(ctxt, finalizeReq) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, batchResp.Batch) | ||
require.Len(t, batchResp.Batch.Assets, len(assetRequests)) | ||
require.Equal( | ||
t, mintrpc.BatchState_BATCH_STATE_BROADCAST, | ||
batchResp.Batch.State, | ||
) | ||
|
||
itest.WaitForBatchState( | ||
t, ctxt, minter, wait.DefaultTimeout, | ||
batchResp.Batch.BatchKey, | ||
mintrpc.BatchState_BATCH_STATE_BROADCAST, | ||
) | ||
hashes, err := itest.WaitForNTxsInMempool( | ||
miner, 1, wait.DefaultTimeout, | ||
) | ||
require.NoError(t, err) | ||
require.GreaterOrEqual(t, len(hashes), 1) | ||
|
||
return itest.ConfirmBatch( | ||
t, miner, minter, assetRequests, sub, *hashes[0], | ||
batchResp.Batch.BatchKey, | ||
) | ||
} | ||
|
||
// getTotalAssetGroups returns the total number of asset groups found in the | ||
// passed array of assets. | ||
func getTotalAssetGroups(assets []*taprpc.Asset) []string { | ||
groups := fn.NewSet[string]() | ||
|
||
for _, v := range assets { | ||
groupKeyStr := fmt.Sprintf("%x", v.AssetGroup.TweakedGroupKey) | ||
groups.Add(groupKeyStr) | ||
} | ||
|
||
return groups.ToSlice() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grammar nit: "If there aren't any existing groups".