Skip to content

Commit a51368f

Browse files
committed
itest: ensure that asset balance does not increase on tapd restart
Bug report: #665
1 parent aa31e22 commit a51368f

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

itest/send_test.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,196 @@ func testBasicSendUnidirectional(t *harnessTest) {
135135
wg.Wait()
136136
}
137137

138+
// testRestartReceiver tests that the receiver node's asset balance after a
139+
// single asset transfer does not change if the receiver node restarts.
140+
// Before the addition of this test, after restarting the receiver node
141+
// the asset balance would be erroneously incremented. This is because the
142+
// receiver node was not storing asset transfer in its database with the
143+
// appropriate field uniqueness constraints.
144+
func testRestartReceiverCheckBalance(t *harnessTest) {
145+
var (
146+
ctxb = context.Background()
147+
wg sync.WaitGroup
148+
)
149+
150+
const (
151+
// Number of units to send.
152+
numUnits = 10
153+
)
154+
155+
// Subscribe to receive assent send events from primary tapd node.
156+
eventNtfns, err := t.tapd.SubscribeSendAssetEventNtfns(
157+
ctxb, &taprpc.SubscribeSendAssetEventNtfnsRequest{},
158+
)
159+
require.NoError(t.t, err)
160+
161+
// Test to ensure that we execute the transaction broadcast state.
162+
// This test is executed in a goroutine to ensure that we can receive
163+
// the event notification from the tapd node as the rest of the test
164+
// proceeds.
165+
wg.Add(1)
166+
go func() {
167+
defer wg.Done()
168+
169+
broadcastState := tapfreighter.SendStateBroadcast.String()
170+
targetEventSelector := func(event *taprpc.SendAssetEvent) bool {
171+
switch eventTyped := event.Event.(type) {
172+
case *taprpc.SendAssetEvent_ExecuteSendStateEvent:
173+
ev := eventTyped.ExecuteSendStateEvent
174+
175+
// Log send state execution.
176+
timestamp := time.UnixMicro(ev.Timestamp)
177+
t.Logf("Executing send state (%v): %v",
178+
timestamp.Format(time.RFC3339Nano),
179+
ev.SendState)
180+
181+
return ev.SendState == broadcastState
182+
}
183+
184+
return false
185+
}
186+
187+
timeout := 2 * defaultProofTransferReceiverAckTimeout
188+
ctx, cancel := context.WithTimeout(ctxb, timeout)
189+
defer cancel()
190+
assertAssetSendNtfsEvent(
191+
t, ctx, eventNtfns, targetEventSelector, 1,
192+
)
193+
}()
194+
195+
// First, we'll make a normal assets with enough units to allow us to
196+
// send it around a few times.
197+
rpcAssets := MintAssetsConfirmBatch(
198+
t.t, t.lndHarness.Miner.Client, t.tapd,
199+
[]*mintrpc.MintAssetRequest{issuableAssets[0]},
200+
)
201+
202+
genInfo := rpcAssets[0].AssetGenesis
203+
204+
// Now that we have the asset created, we'll make a new node that'll
205+
// serve as the node which'll receive the assets. The existing tapd
206+
// node will be used to synchronize universe state.
207+
//
208+
// We will stipulate that the receiver node's custodian service should
209+
// not delay commencing the proof retrieval procedure once a suitable
210+
// on-chain asset transfer is detected. This will ensure that on restart
211+
// the receiver node will attempt to immediately retrieve the asset
212+
// proof even if the proof and asset are present.
213+
custodianProofRetrievalDelay := 0 * time.Second
214+
215+
recvTapd := setupTapdHarness(
216+
t.t, t, t.lndHarness.Bob, t.universeServer,
217+
func(params *tapdHarnessParams) {
218+
params.startupSyncNode = t.tapd
219+
params.startupSyncNumAssets = len(rpcAssets)
220+
params.custodianProofRetrievalDelay = &custodianProofRetrievalDelay
221+
},
222+
)
223+
defer func() {
224+
require.NoError(t.t, recvTapd.stop(!*noDelete))
225+
}()
226+
227+
// Next, we'll attempt to complete two transfers with distinct
228+
// addresses from our main node to Bob.
229+
currentUnits := issuableAssets[0].Asset.Amount
230+
231+
// Issue a single address which will be reused for each send.
232+
bobAddr, err := recvTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
233+
AssetId: genInfo.AssetId,
234+
Amt: numUnits,
235+
AssetVersion: rpcAssets[0].Version,
236+
})
237+
require.NoError(t.t, err)
238+
239+
t.t.Logf("Performing send procedure")
240+
241+
// Deduct what we sent from the expected current number of
242+
// units.
243+
currentUnits -= numUnits
244+
245+
AssertAddrCreated(t.t, recvTapd, rpcAssets[0], bobAddr)
246+
247+
sendResp := sendAssetsToAddr(t, t.tapd, bobAddr)
248+
249+
ConfirmAndAssertOutboundTransfer(
250+
t.t, t.lndHarness.Miner.Client, t.tapd, sendResp,
251+
genInfo.AssetId,
252+
[]uint64{currentUnits, numUnits}, 0, 1,
253+
)
254+
AssertNonInteractiveRecvComplete(t.t, recvTapd, 1)
255+
256+
// Close event stream.
257+
err = eventNtfns.CloseSend()
258+
require.NoError(t.t, err)
259+
260+
wg.Wait()
261+
262+
assertRecvBalance := func() {
263+
// Get asset balance by group from the receiver node.
264+
respGroup, err := recvTapd.ListBalances(
265+
ctxb, &taprpc.ListBalancesRequest{
266+
GroupBy: &taprpc.ListBalancesRequest_GroupKey{
267+
GroupKey: true,
268+
},
269+
},
270+
)
271+
require.NoError(t.t, err)
272+
273+
// We expect to see a single asset group balance. The receiver
274+
// node received one asset only.
275+
require.Len(t.t, respGroup.AssetGroupBalances, 1)
276+
277+
var assetGroupBalance *taprpc.AssetGroupBalance
278+
279+
for _, value := range respGroup.AssetGroupBalances {
280+
assetGroupBalance = value
281+
break
282+
}
283+
284+
require.Equal(t.t, int(10), int(assetGroupBalance.Balance))
285+
286+
// Get asset balance by asset ID from the receiver node.
287+
respAsset, err := recvTapd.ListBalances(
288+
ctxb, &taprpc.ListBalancesRequest{
289+
GroupBy: &taprpc.ListBalancesRequest_AssetId{
290+
AssetId: true,
291+
},
292+
},
293+
)
294+
require.NoError(t.t, err)
295+
296+
// We expect to see a single asset group balance. The receiver
297+
// node received one asset only.
298+
require.Len(t.t, respAsset.AssetBalances, 1)
299+
300+
var assetBalance *taprpc.AssetBalance
301+
302+
for _, value := range respAsset.AssetBalances {
303+
assetBalance = value
304+
break
305+
}
306+
307+
require.Equal(t.t, assetBalance.Balance, uint64(10))
308+
}
309+
310+
// Initial balance check.
311+
assertRecvBalance()
312+
313+
// Restart the receiver node and then check the balance again.
314+
require.NoError(t.t, recvTapd.stop(false))
315+
require.NoError(t.t, recvTapd.start(false))
316+
317+
assertRecvBalance()
318+
319+
// Restart the receiver node, mine some blocks, and then check the
320+
// balance again.
321+
require.NoError(t.t, recvTapd.stop(false))
322+
t.lndHarness.MineBlocks(7)
323+
require.NoError(t.t, recvTapd.start(false))
324+
325+
assertRecvBalance()
326+
}
327+
138328
// testResumePendingPackageSend tests that we can properly resume a pending
139329
// package send after a restart.
140330
func testResumePendingPackageSend(t *harnessTest) {

itest/test_list_on_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ var testCases = []*testCase{
5252
test: testBasicSendUnidirectional,
5353
proofCourierType: proof.UniverseRpcCourierType,
5454
},
55+
{
56+
name: "restart receiver check balance",
57+
test: testRestartReceiverCheckBalance,
58+
proofCourierType: proof.UniverseRpcCourierType,
59+
},
5560
{
5661
name: "resume pending package send",
5762
test: testResumePendingPackageSend,

0 commit comments

Comments
 (0)