Skip to content

Commit 9075e0a

Browse files
committed
itest: add test for zero-value utxo garbage collection
1 parent 2964429 commit 9075e0a

File tree

3 files changed

+182
-27
lines changed

3 files changed

+182
-27
lines changed

itest/burn_test.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,6 @@ func testBurnAssets(t *harnessTest) {
129129
})
130130
require.NoError(t.t, err)
131131

132-
burnRespJSON, err := formatProtoJSON(burnResp)
133-
require.NoError(t.t, err)
134-
t.Logf("Got response from burning %d units: %v", burnAmt, burnRespJSON)
135-
136132
AssertAssetOutboundTransferWithOutputs(
137133
t.t, minerClient, t.tapd, burnResp.BurnTransfer,
138134
[][]byte{simpleAssetGen.AssetId},
@@ -204,10 +200,6 @@ func testBurnAssets(t *harnessTest) {
204200
})
205201
require.NoError(t.t, err)
206202

207-
burnRespJSON, err = formatProtoJSON(burnResp)
208-
require.NoError(t.t, err)
209-
t.Logf("Got response from burning all units: %v", burnRespJSON)
210-
211203
AssertAssetOutboundTransferWithOutputs(
212204
t.t, minerClient, t.tapd, burnResp.BurnTransfer,
213205
[][]byte{simpleCollectibleGen.AssetId}, []uint64{1}, 3, 4, 1,
@@ -235,11 +227,6 @@ func testBurnAssets(t *harnessTest) {
235227
})
236228
require.NoError(t.t, err)
237229

238-
burnRespJSON, err = formatProtoJSON(burnResp)
239-
require.NoError(t.t, err)
240-
t.Logf("Got response from burning units from multiple inputs: %v",
241-
burnRespJSON)
242-
243230
AssertAssetOutboundTransferWithOutputs(
244231
t.t, minerClient, t.tapd, burnResp.BurnTransfer,
245232
[][]byte{simpleAssetGen.AssetId},
@@ -281,11 +268,6 @@ func testBurnAssets(t *harnessTest) {
281268
})
282269
require.NoError(t.t, err)
283270

284-
burnRespJSON, err = formatProtoJSON(burnResp)
285-
require.NoError(t.t, err)
286-
t.Logf("Got response from burning units from grouped asset: %v",
287-
burnRespJSON)
288-
289271
AssertAssetOutboundTransferWithOutputs(
290272
t.t, minerClient, t.tapd, burnResp.BurnTransfer,
291273
[][]byte{simpleGroupGen.AssetId},
@@ -343,11 +325,6 @@ func testBurnAssets(t *harnessTest) {
343325
})
344326
require.NoError(t.t, err)
345327

346-
burnRespJSON, err = formatProtoJSON(burnResp)
347-
require.NoError(t.t, err)
348-
t.Logf("Got response from burning units from grouped asset: %v",
349-
burnRespJSON)
350-
351328
AssertAssetOutboundTransferWithOutputs(
352329
t.t, minerClient, t.tapd, burnResp.BurnTransfer,
353330
[][]byte{simpleGroupCollectGen.AssetId}, []uint64{1}, 6, 7, 1,
@@ -459,10 +436,6 @@ func testBurnGroupedAssets(t *harnessTest) {
459436
})
460437
require.NoError(t.t, err)
461438

462-
burnRespJSON, err := formatProtoJSON(burnResp)
463-
require.NoError(t.t, err)
464-
t.Logf("Got response from burning %d units: %v", burnAmt, burnRespJSON)
465-
466439
// Assert that the asset burn transfer occurred correctly.
467440
AssertAssetOutboundTransferWithOutputs(
468441
t.t, miner, t.tapd, burnResp.BurnTransfer,

itest/test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ var allTestCases = []*testCase{
101101
name: "min relay fee bump",
102102
test: testMinRelayFeeBump,
103103
},
104+
{
105+
name: "zero value anchor sweep",
106+
test: testZeroValueAnchorSweep,
107+
},
104108
{
105109
name: "restart receiver check balance",
106110
test: testRestartReceiverCheckBalance,

itest/zero_value_anchor_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package itest
2+
3+
import (
4+
"context"
5+
6+
"github.com/lightninglabs/taproot-assets/asset"
7+
"github.com/lightninglabs/taproot-assets/taprpc"
8+
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// testZeroValueAnchorSweep tests that zero-value anchor outputs
13+
// are automatically swept when creating new on-chain transactions.
14+
func testZeroValueAnchorSweep(t *harnessTest) {
15+
ctxb := context.Background()
16+
17+
// First, mint some simple asset.
18+
rpcAssets := MintAssetsConfirmBatch(
19+
t.t, t.lndHarness.Miner().Client, t.tapd,
20+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
21+
)
22+
genInfo := rpcAssets[0].AssetGenesis
23+
assetAmount := simpleAssets[0].Asset.Amount
24+
25+
// Create a second tapd node.
26+
bobLnd := t.lndHarness.NewNodeWithCoins("Bob", nil)
27+
secondTapd := setupTapdHarness(t.t, t, bobLnd, t.universeServer)
28+
defer func() {
29+
require.NoError(t.t, secondTapd.stop(!*noDelete))
30+
}()
31+
32+
bobAddr, err := secondTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
33+
AssetId: genInfo.AssetId,
34+
Amt: assetAmount,
35+
AssetVersion: rpcAssets[0].Version,
36+
})
37+
require.NoError(t.t, err)
38+
39+
// Send ALL assets to Bob, which should create a tombstone.
40+
sendResp, _ := sendAssetsToAddr(t, t.tapd, bobAddr)
41+
42+
ConfirmAndAssertOutboundTransfer(
43+
t.t, t.lndHarness.Miner().Client, t.tapd, sendResp,
44+
genInfo.AssetId,
45+
[]uint64{0, assetAmount}, 0, 1,
46+
)
47+
AssertNonInteractiveRecvComplete(t.t, secondTapd, 1)
48+
49+
// Alice should have 1 tombstone UTXO from the full-value send.
50+
AssertBalances(
51+
t.t, t.tapd, 0, WithScriptKeyType(asset.ScriptKeyTombstone),
52+
WithNumUtxos(1), WithNumAnchorUtxos(1),
53+
)
54+
55+
// Test 1: Send transaction sweeps tombstones.
56+
rpcAssets2 := MintAssetsConfirmBatch(
57+
t.t, t.lndHarness.Miner().Client, t.tapd,
58+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
59+
)
60+
genInfo2 := rpcAssets2[0].AssetGenesis
61+
62+
// Send full amount of the new asset. This should sweep Alice's
63+
// first tombstone and create a new one.
64+
bobAddr2, err := secondTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
65+
AssetId: genInfo2.AssetId,
66+
Amt: assetAmount,
67+
AssetVersion: rpcAssets2[0].Version,
68+
})
69+
require.NoError(t.t, err)
70+
71+
sendResp2, _ := sendAssetsToAddr(t, t.tapd, bobAddr2)
72+
73+
ConfirmAndAssertOutboundTransfer(
74+
t.t, t.lndHarness.Miner().Client, t.tapd, sendResp2,
75+
genInfo2.AssetId,
76+
[]uint64{0, assetAmount}, 1, 2,
77+
)
78+
AssertNonInteractiveRecvComplete(t.t, secondTapd, 2)
79+
80+
// Check Alice's tombstone balance. The first tombstone should have been
81+
// swept (spent on-chain as an input), and a new one created. We now
82+
// have 1 tombstone UTXO (the new one from the second send).
83+
AssertBalances(
84+
t.t, t.tapd, 0, WithScriptKeyType(asset.ScriptKeyTombstone),
85+
WithNumUtxos(1), WithNumAnchorUtxos(1),
86+
)
87+
88+
// Get the new tombstone outpoint.
89+
utxosAfterSend, err := t.tapd.ListUtxos(ctxb, &taprpc.ListUtxosRequest{
90+
ScriptKeyType: &taprpc.ScriptKeyTypeQuery{
91+
Type: &taprpc.ScriptKeyTypeQuery_ExplicitType{
92+
ExplicitType: taprpc.
93+
ScriptKeyType_SCRIPT_KEY_TOMBSTONE,
94+
},
95+
},
96+
})
97+
require.NoError(t.t, err)
98+
require.Len(t.t, utxosAfterSend.ManagedUtxos, 1)
99+
100+
// Test 2: Burning transaction sweeps tombstones.
101+
rpcAssets3 := MintAssetsConfirmBatch(
102+
t.t, t.lndHarness.Miner().Client, t.tapd,
103+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
104+
)
105+
genInfo3 := rpcAssets3[0].AssetGenesis
106+
107+
// Full burn the asset to create a zero-value burn UTXO
108+
// and sweep the second tombstone.
109+
burnResp, err := t.tapd.BurnAsset(ctxb, &taprpc.BurnAssetRequest{
110+
Asset: &taprpc.BurnAssetRequest_AssetId{
111+
AssetId: genInfo3.AssetId,
112+
},
113+
AmountToBurn: assetAmount,
114+
ConfirmationText: "assets will be destroyed",
115+
})
116+
require.NoError(t.t, err)
117+
118+
AssertAssetOutboundTransferWithOutputs(
119+
t.t, t.lndHarness.Miner().Client, t.tapd, burnResp.BurnTransfer,
120+
[][]byte{genInfo3.AssetId},
121+
[]uint64{assetAmount}, 2, 3, 1, true,
122+
)
123+
124+
// Alice should have 0 tombstones remaining and 1 burn UTXO.
125+
AssertBalances(
126+
t.t, t.tapd, 0, WithScriptKeyType(asset.ScriptKeyTombstone),
127+
WithNumUtxos(0), WithNumAnchorUtxos(0),
128+
)
129+
AssertBalances(
130+
t.t, t.tapd, assetAmount,
131+
WithScriptKeyType(asset.ScriptKeyBurn),
132+
WithNumUtxos(1), WithNumAnchorUtxos(1),
133+
)
134+
135+
// Get the burn UTXO outpoint for the next test.
136+
burnUtxos, err := t.tapd.ListUtxos(ctxb, &taprpc.ListUtxosRequest{
137+
ScriptKeyType: &taprpc.ScriptKeyTypeQuery{
138+
Type: &taprpc.ScriptKeyTypeQuery_ExplicitType{
139+
ExplicitType: taprpc.
140+
ScriptKeyType_SCRIPT_KEY_BURN,
141+
},
142+
},
143+
})
144+
require.NoError(t.t, err)
145+
require.Len(t.t, burnUtxos.ManagedUtxos, 1)
146+
147+
// Test 3: Send transactions sweeps zero-value burns.
148+
rpcAssets4 := MintAssetsConfirmBatch(
149+
t.t, t.lndHarness.Miner().Client, t.tapd,
150+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
151+
)
152+
genInfo4 := rpcAssets4[0].AssetGenesis
153+
154+
// Send partial amount. This should NOT create a tombstone output
155+
// and sweep the burn UTXO.
156+
partialAmount := assetAmount / 2
157+
bobAddr3, err := secondTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
158+
AssetId: genInfo4.AssetId,
159+
Amt: partialAmount,
160+
AssetVersion: rpcAssets4[0].Version,
161+
})
162+
require.NoError(t.t, err)
163+
164+
sendResp3, _ := sendAssetsToAddr(t, t.tapd, bobAddr3)
165+
166+
ConfirmAndAssertOutboundTransfer(
167+
t.t, t.lndHarness.Miner().Client, t.tapd, sendResp3,
168+
genInfo4.AssetId,
169+
[]uint64{partialAmount, partialAmount}, 3, 4,
170+
)
171+
AssertNonInteractiveRecvComplete(t.t, secondTapd, 3)
172+
173+
// The burn UTXO should have been swept.
174+
AssertBalances(
175+
t.t, t.tapd, 0, WithScriptKeyType(asset.ScriptKeyBurn),
176+
WithNumUtxos(0), WithNumAnchorUtxos(0),
177+
)
178+
}

0 commit comments

Comments
 (0)