Skip to content

Commit 504440f

Browse files
committed
tappsbt+itest: make V2 address transfers interactive
Because we don't need to be able to fully predict the asset leaf as it is put on chain (which was the case for V0 and V1 addresses), we can optimize chain usage by avoiding the creation of a tombstone output on full value sends (meaning we use an interactive transfer scheme for V2 addresses, since the receiver will learn about the proofs from the auth mailbox and doesn't need to predict the leave(s) themselves). That means we no longer have to create a zero-value change output that locks 1k sats until we implement garbage collection if the user wants to send all assets. So fewer outputs created on-chain and fewer sats locked for the user.
1 parent e1da4bb commit 504440f

File tree

3 files changed

+54
-47
lines changed

3 files changed

+54
-47
lines changed

itest/addrs_v2_test.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ func testAddressV2WithGroupKey(t *harnessTest) {
271271
AssertAssetOutboundTransferWithOutputs(
272272
t.t, t.lndHarness.Miner().Client, t.tapd,
273273
sendResp.Transfer, [][]byte{firstAssetID, secondAssetID},
274-
[]uint64{0, firstAsset.Amount, 0, secondAsset.Amount}, 0,
275-
1, 4, true,
274+
[]uint64{firstAsset.Amount, secondAsset.Amount}, 0, 1, 2, true,
276275
)
277276
AssertReceiveEventsCustom(t.t, addrEvents, []taprpc.AddrEventStatus{
278277
statusConfirmed,
@@ -284,13 +283,6 @@ func testAddressV2WithGroupKey(t *harnessTest) {
284283
WithNumUtxos(2),
285284
)
286285

287-
// The sending node should only have two tombstone outputs, but the
288-
// total value should be zero.
289-
AssertBalanceByGroup(
290-
t.t, t.tapd, hex.EncodeToString(groupKey), 0, WithNumUtxos(2),
291-
WithAllScriptKeyTypes(),
292-
)
293-
294286
// We now make sure we can spend those assets again by sending
295287
// them back to ourselves, using an address with an amount.
296288
groupAddrBob2, _ := NewAddrWithEventStream(
@@ -354,8 +346,8 @@ func testAddressV2WithGroupKey(t *harnessTest) {
354346
AssertAssetOutboundTransferWithOutputs(
355347
t.t, t.lndHarness.Miner().Client, bobTapd,
356348
sendResp3.Transfer, [][]byte{firstAssetID, secondAssetID},
357-
[]uint64{0, totalAmount/2 - 50, 50, 0, totalAmount / 2}, 1,
358-
2, 5, true,
349+
[]uint64{totalAmount/2 - 50, 50, totalAmount / 2}, 1, 2, 3,
350+
true,
359351
)
360352
AssertBalanceByGroup(
361353
t.t, t.tapd, hex.EncodeToString(groupKey), totalAmount,
@@ -493,8 +485,8 @@ func testAddressV2WithGroupKeyMultipleRoundTrips(t *harnessTest) {
493485
AssertAssetOutboundTransferWithOutputs(
494486
t.t, t.lndHarness.Miner().Client, t.tapd,
495487
sendResp.Transfer, [][]byte{firstAssetID},
496-
[]uint64{0, firstAsset.Amount}, currentTransferIdx,
497-
numTransfers, 2, true,
488+
[]uint64{firstAsset.Amount}, currentTransferIdx,
489+
numTransfers, 1, true,
498490
)
499491

500492
AssertAddrEventByStatus(t.t, bobTapd, statusCompleted, numTransfers)
@@ -516,8 +508,8 @@ func testAddressV2WithGroupKeyMultipleRoundTrips(t *harnessTest) {
516508
AssertAssetOutboundTransferWithOutputs(
517509
t.t, t.lndHarness.Miner().Client, t.tapd,
518510
sendResp.Transfer, [][]byte{secondAssetID},
519-
[]uint64{0, secondAsset.Amount}, currentTransferIdx,
520-
numTransfers, 2, true,
511+
[]uint64{secondAsset.Amount}, currentTransferIdx,
512+
numTransfers, 1, true,
521513
)
522514

523515
AssertAddrEventByStatus(t.t, bobTapd, statusCompleted, numTransfers)
@@ -539,8 +531,8 @@ func testAddressV2WithGroupKeyMultipleRoundTrips(t *harnessTest) {
539531
AssertAssetOutboundTransferWithOutputs(
540532
t.t, t.lndHarness.Miner().Client, t.tapd,
541533
sendResp.Transfer, [][]byte{thirdAssetID},
542-
[]uint64{0, thirdAsset.Amount}, currentTransferIdx,
543-
numTransfers, 2, true,
534+
[]uint64{thirdAsset.Amount}, currentTransferIdx,
535+
numTransfers, 1, true,
544536
)
545537

546538
AssertAddrEventByStatus(t.t, bobTapd, statusCompleted, numTransfers)
@@ -644,8 +636,8 @@ func testAddressV2WithGroupKeyMultipleRoundTrips(t *harnessTest) {
644636
AssertAssetOutboundTransferWithOutputs(
645637
t.t, t.lndHarness.Miner().Client, t.tapd,
646638
sendResp.Transfer, [][]byte{assetIDs[idx]},
647-
[]uint64{0, amount}, currentTransferIdx, numTransfers,
648-
2, true,
639+
[]uint64{amount}, currentTransferIdx, numTransfers, 1,
640+
true,
649641
)
650642

651643
AssertAddrEventByStatus(

tappsbt/create.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,47 @@ func FromAddresses(receiverAddrs []*address.Tap,
3939
switch firstAddr.Version {
4040
case address.V0:
4141
pkt.Version = V0
42-
case address.V1, address.V2:
42+
case address.V1:
4343
pkt.Version = V1
44+
case address.V2:
45+
pkt.Version = V1
46+
47+
// All sends to V0 and V1 addresses _NEED_ to be non-
48+
// interactive, because the receiver needs to be able to predict
49+
// the final on-chain Taproot output key all the way up,
50+
// starting at the asset leaf, assuming a split output.
51+
// For V2 addresses that is no longer the case, since we are
52+
// relying on the output list to be sent in a separate message
53+
// and the proofs being fetched from a universe server. So we
54+
// can use interactive (e.g. full value sends without tombstone)
55+
// sends to V2 addresses.
56+
for idx := range receiverAddrs {
57+
addr := receiverAddrs[idx]
58+
if addr.Version != firstAddr.Version {
59+
return nil, fmt.Errorf("mixed address versions")
60+
}
61+
62+
//nolint:lll
63+
pkt.Outputs = append(pkt.Outputs, &VOutput{
64+
AssetVersion: addr.AssetVersion,
65+
Amount: addr.Amount,
66+
Interactive: true,
67+
// We can directly use the index as the anchor
68+
// output here, a change output will be added
69+
// by the funding logic at the end if necessary.
70+
AnchorOutputIndex: uint32(idx),
71+
ScriptKey: asset.NewScriptKey(
72+
&addr.ScriptKey,
73+
),
74+
AnchorOutputInternalKey: &addr.InternalKey,
75+
AnchorOutputTapscriptSibling: addr.TapscriptSibling,
76+
ProofDeliveryAddress: &addr.ProofCourierAddr,
77+
Address: addr,
78+
})
79+
}
80+
81+
return pkt, nil
82+
4483
default:
4584
return nil, address.ErrUnknownVersion
4685
}

tappsbt/testdata/psbt_encoding_generated.json

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,12 @@
2828
}
2929
],
3030
"outputs": [
31-
{
32-
"amount": 0,
33-
"type": 1,
34-
"asset_version": 0,
35-
"interactive": false,
36-
"anchor_output_index": 0,
37-
"anchor_output_internal_key": "",
38-
"anchor_output_bip32_derivation": null,
39-
"anchor_output_tr_bip32_derivation": null,
40-
"anchor_output_tapscript_sibling": "",
41-
"asset": null,
42-
"split_asset": null,
43-
"pk_script": "51207c79b9b26e463895eef5679d8558942c86c4ad2233adef01bc3e6d540b3653fe",
44-
"bip32_derivation": null,
45-
"tr_bip32_derivation": null,
46-
"tr_internal_key": "",
47-
"tr_merkle_root": "",
48-
"proof_delivery_address": "",
49-
"proof_suffix": null,
50-
"relative_lock_time": 0,
51-
"lock_time": 0,
52-
"alt_leaves": null,
53-
"address": null
54-
},
5531
{
5632
"amount": 1,
5733
"type": 0,
5834
"asset_version": 1,
59-
"interactive": false,
60-
"anchor_output_index": 1,
35+
"interactive": true,
36+
"anchor_output_index": 0,
6137
"anchor_output_internal_key": "02daf785f20dcb5bb06bef717296c3894ed2564d5e3f8325d2f6bad1315aaad16c",
6238
"anchor_output_bip32_derivation": null,
6339
"anchor_output_tr_bip32_derivation": null,
@@ -116,7 +92,7 @@
11692
"version": 1,
11793
"chain_params_hrp": "tapbc"
11894
},
119-
"expected": "cHNidP8BALQCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAIlEgfHm5sm5GOJXu9WedhViULIbErSIzre8BvD5tVAs2U/4BAAAAAAAAACJRIGgYBPh4kJp3/4EzkUHRtV02t2os1/Ccg4oetn/qmDcAAAAAAAAAAAAiUSBjFU6eReYqJ8Q/KzNCsMSbii9J0CY9zBjzQXK2as/vCQAAAAABcAEBAXEFdGFwYmMBcgEBAAFwZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXEIAAAAAAAAAAABcgABcwgAAAAAAAAAAAF1AAF4AAABcAEBAXEBAAFyCAAAAAAAAAAAAXkBAAF8CAAAAAAAAAAAAX0IAAAAAAAAAAAAAXABAAFxAQABcggAAAAAAAAAAQFzIQLa94XyDctbsGvvcXKWw4lO0lZNXj+DJdL2utExWqrRbAF4FQDAEm5vdCBhIHZhbGlkIHNjcmlwdAF5AQEBeidhdXRobWFpbGJveCt1bml2ZXJzZXJwYzovL2Zvby5iYXI6MTAwMjkBfAgAAAAAAAAAAAF9CAAAAAAAAAAAAX+yAAECAgEBBSECL4A3VoZ30SIMCONSXooiWNo0baEd84sWe3Yv8I5t4KEGIQJoGAT4eJCad/+BM5FB0bVdNrdqLNfwnIOKHrZ/6pg3AAghAtr3hfINy1uwa+9xcpbDiU7SVk1eP4Ml0va60TFaqtFsCRUAwBJub3QgYSB2YWxpZCBzY3JpcHQKAQEMJ2F1dGhtYWlsYm94K3VuaXZlcnNlcnBjOi8vZm9vLmJhcjoxMDAyOQABcAEAAXEBAAFyCAAAAAAAAAAAAXkBAAF8CAAAAAAAAAAAAX0IAAAAAAAAAAAA",
95+
"expected": "cHNidP8BAIkCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAIlEgaBgE+HiQmnf/gTORQdG1XTa3aizX8JyDih62f+qYNwAAAAAAAAAAACJRIGMVTp5F5ionxD8rM0KwxJuKL0nQJj3MGPNBcrZqz+8JAAAAAAFwAQEBcQV0YXBiYwFyAQEAAXBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcQgAAAAAAAAAAAFyAAFzCAAAAAAAAAAAAXUAAXgAAAFwAQABcQEBAXIIAAAAAAAAAAABcyEC2veF8g3LW7Br73FylsOJTtJWTV4/gyXS9rrRMVqq0WwBeBUAwBJub3QgYSB2YWxpZCBzY3JpcHQBeQEBAXonYXV0aG1haWxib3grdW5pdmVyc2VycGM6Ly9mb28uYmFyOjEwMDI5AXwIAAAAAAAAAAABfQgAAAAAAAAAAAF/sgABAgIBAQUhAi+AN1aGd9EiDAjjUl6KIljaNG2hHfOLFnt2L/CObeChBiECaBgE+HiQmnf/gTORQdG1XTa3aizX8JyDih62f+qYNwAIIQLa94XyDctbsGvvcXKWw4lO0lZNXj+DJdL2utExWqrRbAkVAMASbm90IGEgdmFsaWQgc2NyaXB0CgEBDCdhdXRobWFpbGJveCt1bml2ZXJzZXJwYzovL2Zvby5iYXI6MTAwMjkAAXABAAFxAQABcggAAAAAAAAAAAF5AQABfAgAAAAAAAAAAAF9CAAAAAAAAAAAAA==",
12096
"comment": "minimal packet"
12197
},
12298
{

0 commit comments

Comments
 (0)