Skip to content

Commit ab2d70d

Browse files
author
ffranr
authored
Merge pull request #1331 from GeorgeTsagk/loadtest-send-v2
Add loadtest `sendV2`
2 parents 303f3da + 642ec6f commit ab2d70d

File tree

6 files changed

+219
-14
lines changed

6 files changed

+219
-14
lines changed

itest/loadtest/config.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/btcsuite/btcd/chaincfg"
88
"github.com/jessevdk/go-flags"
9-
"github.com/lightninglabs/taproot-assets/taprpc"
109
)
1110

1211
const (
@@ -111,9 +110,10 @@ type Config struct {
111110
// This is only relevant for the send test.
112111
NumAssets uint64 `long:"send-test-num-assets" description:"the number of assets to send in each send operation; only relevant for the send test"`
113112

114-
// SendType is the type of asset to attempt to send. This is only
115-
// relevant for the send test.
116-
SendType taprpc.AssetType `long:"send-test-send-type" description:"the type of asset to attempt to send; only relevant for the send test"`
113+
// SendAssetType is the type of asset to attempt to send. This is only
114+
// relevant for the send test. Acceptable values are "normal" and
115+
// "collectible".
116+
SendAssetType string `long:"send-asset-type" description:"the type of asset to attempt to send; only relevant for the send test"`
117117

118118
// TestSuiteTimeout is the timeout for the entire test suite.
119119
TestSuiteTimeout time.Duration `long:"test-suite-timeout" description:"the timeout for the entire test suite"`
@@ -143,8 +143,8 @@ func DefaultConfig() Config {
143143
Network: "regtest",
144144
BatchSize: 100,
145145
NumSends: 50,
146-
NumAssets: 1, // We only mint collectibles.
147-
SendType: taprpc.AssetType_COLLECTIBLE,
146+
NumAssets: 1,
147+
SendAssetType: "normal",
148148
TestSuiteTimeout: defaultSuiteTimeout,
149149
TestTimeout: defaultTestTimeout,
150150
PrometheusGateway: &PrometheusGatewayConfig{

itest/loadtest/load_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ var loadTestCases = []testCase{
4747
name: "send",
4848
fn: sendTest,
4949
},
50+
{
51+
name: "sendV2",
52+
fn: sendTestV2,
53+
},
5054
{
5155
name: "multisig",
5256
fn: multisigTest,

itest/loadtest/loadtest-sample.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ send-test-num-sends=5
2222
# Number of assets to send in each send operation for send test
2323
send-test-num-assets=1
2424

25+
# Type of asset to use in the test case. For V2 test cases, a "normal" type is
26+
# required.
27+
send-asset-type="normal"
28+
2529
# Timeout for the entire test suite
2630
test-suite-timeout=120m
2731

itest/loadtest/send_test.go

Lines changed: 156 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
prand "math/rand"
7+
"math/rand/v2"
78
"testing"
89
"time"
910

@@ -29,30 +30,180 @@ func sendTest(t *testing.T, ctx context.Context, cfg *Config) {
2930
ctxt, cancel := context.WithTimeout(ctxb, cfg.TestTimeout)
3031
defer cancel()
3132

33+
sendType := stringToAssetType(cfg.SendAssetType)
34+
3235
t.Logf("Running send test, sending %d asset(s) of type %v %d times",
33-
cfg.NumAssets, cfg.SendType, cfg.NumSends)
36+
cfg.NumAssets, sendType, cfg.NumSends)
3437
for i := 1; i <= cfg.NumSends; i++ {
3538
send, receive, ok := pickSendNode(
36-
t, ctx, cfg.NumAssets, cfg.SendType, alice, bob,
39+
t, ctx, cfg.NumAssets, sendType, alice, bob,
3740
)
3841
if !ok {
3942
t.Fatalf("Aborting send test at attempt %d of %d as "+
4043
"no node has enough balance to send %d "+
4144
"assets of type %v", i, cfg.NumSends,
42-
cfg.NumAssets, cfg.SendType)
45+
cfg.NumAssets, sendType)
4346
return
4447
}
4548

4649
sendAssets(
47-
t, ctxt, cfg.NumAssets, cfg.SendType, send, receive,
50+
t, ctxt, cfg.NumAssets, sendType, send, receive,
4851
bitcoinClient, cfg.TestTimeout,
4952
)
5053

5154
t.Logf("Finished %d of %d send operations", i, cfg.NumSends)
5255
}
5356
}
5457

55-
// sendAssets sends the given number of assets of the given type from the given
58+
// sendTestV2 checks that we are able to send assets between the two nodes. It
59+
// is a more performant and lightweight version of sendTest, as it uses less
60+
// assertions and RPC calls.
61+
func sendTestV2(t *testing.T, ctx context.Context, cfg *Config) {
62+
// Start by initializing all our client connections.
63+
alice, bob, bitcoinClient := initClients(t, ctx, cfg)
64+
65+
ctxb := context.Background()
66+
ctxt, cancel := context.WithTimeout(ctxb, cfg.TestTimeout)
67+
defer cancel()
68+
69+
sendType := stringToAssetType(cfg.SendAssetType)
70+
71+
// Alice is set to be the minter in mintV2, so we use Alice's universe.
72+
uniHost := fmt.Sprintf("%s:%d", alice.cfg.Host, alice.cfg.Port)
73+
74+
// Let's make sure Bob is aware of all the assets that Alice may have
75+
// minted.
76+
itest.SyncUniverses(
77+
ctx, t, bob, alice, uniHost, cfg.TestTimeout,
78+
itest.WithSyncMode(itest.SyncModeFull),
79+
)
80+
81+
// We now retrieve Alice and Bob's balances just once, and will re-use
82+
// them in future function calls. Any update to the balances will be
83+
// directly applied to these response objects, to skip future calls to
84+
// ListBalances.
85+
resAlice, err := alice.ListBalances(ctx, &taprpc.ListBalancesRequest{
86+
GroupBy: &taprpc.ListBalancesRequest_AssetId{
87+
AssetId: true,
88+
},
89+
})
90+
require.NoError(t, err)
91+
92+
resBob, err := bob.ListBalances(ctx, &taprpc.ListBalancesRequest{
93+
GroupBy: &taprpc.ListBalancesRequest_AssetId{
94+
AssetId: true,
95+
},
96+
})
97+
require.NoError(t, err)
98+
99+
for i := 1; i <= cfg.NumSends; i++ {
100+
var (
101+
sender, receiver *rpcClient
102+
senderAssets map[string]*taprpc.AssetBalance
103+
)
104+
105+
// Assets may be sent in both directions, so we make a random
106+
// draw to conclude who the sender is.
107+
draw := rand.IntN(2)
108+
109+
switch draw {
110+
case 0:
111+
sender = alice
112+
senderAssets = resAlice.AssetBalances
113+
receiver = bob
114+
115+
case 1:
116+
sender = bob
117+
senderAssets = resBob.AssetBalances
118+
receiver = alice
119+
}
120+
121+
sendAssetV2(
122+
t, ctxt, cfg.NumAssets, sendType, senderAssets,
123+
sender, receiver, bitcoinClient, cfg.TestTimeout,
124+
)
125+
}
126+
}
127+
128+
// sendAssetV2 sends a certain amount of assets of a specific type from a sender
129+
// to a receiver. It will scan the balance of the sender and find a suitable
130+
// asset to carry out the send, then will dispatch the send and assert its
131+
// completion.
132+
func sendAssetV2(t *testing.T, ctx context.Context, numAssets uint64,
133+
assetType taprpc.AssetType, assets map[string]*taprpc.AssetBalance,
134+
sender, receiver *rpcClient, bitcoinClient *rpcclient.Client,
135+
timeout time.Duration) {
136+
137+
// Look over the sender's balances to see if any asset balance qualifies
138+
// for this send.
139+
var (
140+
assetID []byte
141+
balance *taprpc.AssetBalance
142+
)
143+
for _, v := range assets {
144+
if v.Balance >= numAssets &&
145+
v.AssetGenesis.AssetType == assetType {
146+
147+
assetID = v.AssetGenesis.AssetId
148+
balance = v
149+
150+
break
151+
}
152+
}
153+
154+
// No balance satisfies the amount of this send, we can skip this round.
155+
if assetID == nil {
156+
t.Logf("%s could not send %v assets, no available balance",
157+
sender.cfg.Name, numAssets)
158+
159+
return
160+
}
161+
162+
t.Logf("%s sending %v assets to %s", sender.cfg.Name, numAssets,
163+
receiver.cfg.Name)
164+
165+
// Receiver creates the address to receive the assets.
166+
addr, err := receiver.NewAddr(ctx, &taprpc.NewAddrRequest{
167+
AssetId: assetID,
168+
Amt: numAssets,
169+
ProofCourierAddr: fmt.Sprintf(
170+
"%s://%s:%d", proof.UniverseRpcCourierType,
171+
sender.cfg.Host, sender.cfg.Port,
172+
),
173+
})
174+
require.NoError(t, err)
175+
176+
t.Logf("%s created address %v", receiver.cfg.Name, addr.String())
177+
178+
// Sender initiates the send.
179+
_, err = sender.SendAsset(ctx, &taprpc.SendAssetRequest{
180+
TapAddrs: []string{addr.Encoded},
181+
})
182+
require.NoError(t, err)
183+
t.Logf("%s sent assets to address %v", sender.cfg.Name, addr.String())
184+
185+
// We assert the receiver detects the spend.
186+
itest.AssertAddrEventCustomTimeout(
187+
t, receiver, addr, 1, statusDetected, timeout,
188+
)
189+
t.Logf("%s detected send", receiver.cfg.Name)
190+
191+
// Mine a block to confirm the transfer.
192+
itest.MineBlocks(t, bitcoinClient, 1, 0)
193+
t.Log("Mined 1 block")
194+
195+
// Assert that the transfer is now completed
196+
itest.AssertAddrEventCustomTimeout(
197+
t, receiver, addr, 1, statusCompleted, timeout,
198+
)
199+
t.Logf("%s completed send of %v assets", sender.cfg.Name, numAssets)
200+
201+
// If everything completed correctly, subtract the asset amount from the
202+
// sender's asset balance.
203+
balance.Balance -= numAssets
204+
}
205+
206+
// sendAsset sends the given number of assets of the given type from the given
56207
// node to the other node.
57208
func sendAssets(t *testing.T, ctx context.Context, numAssets uint64,
58209
assetType taprpc.AssetType, send, receive *rpcClient,

itest/loadtest/utils.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,15 @@ func getBitcoinConn(t *testing.T, cfg *BitcoinConfig) *rpcclient.Client {
289289

290290
return client
291291
}
292+
293+
// stringToAssetType converts a string of an asset type to its respective taprpc
294+
// type enum value.
295+
func stringToAssetType(t string) taprpc.AssetType {
296+
switch t {
297+
case "collectible":
298+
return taprpc.AssetType_COLLECTIBLE
299+
300+
default:
301+
return taprpc.AssetType_NORMAL
302+
}
303+
}

itest/utils.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ var (
4242
regtestParams = &chaincfg.RegressionNetParams
4343
)
4444

45+
const (
46+
SyncModeIssuance = universerpc.UniverseSyncMode_SYNC_ISSUANCE_ONLY
47+
SyncModeFull = universerpc.UniverseSyncMode_SYNC_FULL
48+
)
49+
4550
// ClientEventStream is a generic interface for a client stream that allows us
4651
// to receive events from a server.
4752
type ClientEventStream[T any] interface {
@@ -834,15 +839,45 @@ func MintAssetExternalSigner(t *harnessTest, tapNode *tapdHarness,
834839
return batchAssets
835840
}
836841

842+
// syncOptions is a struct that is used to customize the way we perform a
843+
// universe sync.
844+
type syncOptions struct {
845+
syncMode universerpc.UniverseSyncMode
846+
}
847+
848+
// defaultSyncOptions returns the default syncOptions.
849+
func defaultSyncOptions() *syncOptions {
850+
return &syncOptions{
851+
syncMode: SyncModeIssuance,
852+
}
853+
}
854+
855+
// SyncUniverseOpt is used to modify the parameters of a universe sync.
856+
type SyncUniverseOpt func(*syncOptions)
857+
858+
// WithSyncMode can be used to define which sync mode to be used when performing
859+
// a universe sync.
860+
func WithSyncMode(mode universerpc.UniverseSyncMode) SyncUniverseOpt {
861+
return func(so *syncOptions) {
862+
so.syncMode = mode
863+
}
864+
}
865+
837866
// SyncUniverses syncs the universes of two tapd instances and waits until they
838867
// are in sync.
839868
func SyncUniverses(ctx context.Context, t *testing.T, clientTapd,
840869
universeTapd commands.RpcClientsBundle, universeHost string,
841-
timeout time.Duration) {
870+
timeout time.Duration, opts ...SyncUniverseOpt) {
842871

843872
ctxt, cancel := context.WithTimeout(ctx, timeout)
844873
defer cancel()
845874

875+
options := defaultSyncOptions()
876+
877+
for _, opt := range opts {
878+
opt(options)
879+
}
880+
846881
_, err := clientTapd.AddFederationServer(
847882
ctxt, &universerpc.AddFederationServerRequest{
848883
Servers: []*universerpc.UniverseFederationServer{
@@ -863,10 +898,9 @@ func SyncUniverses(ctx context.Context, t *testing.T, clientTapd,
863898
// If we've already added the server in a previous run, we'll
864899
// just need to kick off a sync (as that would otherwise be done
865900
// by adding the server request already).
866-
mode := universerpc.UniverseSyncMode_SYNC_ISSUANCE_ONLY
867901
_, err := clientTapd.SyncUniverse(ctxt, &universerpc.SyncRequest{
868902
UniverseHost: universeHost,
869-
SyncMode: mode,
903+
SyncMode: options.syncMode,
870904
})
871905
require.NoError(t, err)
872906
}

0 commit comments

Comments
 (0)