Skip to content

Commit 89bf4d6

Browse files
committed
itest: extend multi-input PSBT spend test to include partial value spend
This commit extends the existing multi-input PSBT spend test to also include a partial value spend.
1 parent 76cef1b commit 89bf4d6

File tree

2 files changed

+180
-24
lines changed

2 files changed

+180
-24
lines changed

itest/assertions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ func AssertAssetOutboundTransferWithOutputs(t *testing.T,
751751
outpoints := make(map[string]struct{})
752752
scripts := make(map[string]struct{})
753753
for _, o := range outputs {
754+
// Ensure that each transfer output script key is unique.
754755
_, ok := scripts[string(o.ScriptKey)]
755756
require.False(t, ok)
756757

itest/psbt_test.go

Lines changed: 179 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -954,14 +954,16 @@ func testPsbtMultiSend(t *harnessTest) {
954954
}
955955

956956
// testMultiInputPsbtSingleAssetID tests to ensure that we can correctly
957-
// construct and spend a multi-input full value PSBT where each input has the
958-
// same asset ID.
957+
// construct and spend a multi-input partial value and full value PSBT where
958+
// each input has the same asset ID.
959959
//
960960
// The test works as follows:
961961
// 1. Mint an asset on the primary tapd node.
962-
// 2. Send the asset to a secondary tapd node in two different send events.
963-
// 3. Send the asset back to the primary tapd node in a single multi-input PSBT
964-
// send event.
962+
// 2. Send the asset to a secondary tapd node in three different send events.
963+
// 3. Send a partial amount of the asset back to the primary tapd node in a
964+
// single multi-input PSBT send event.
965+
// 4. Send the remaining amount of the asset back to the primary tapd node in
966+
// a single full value multi-input PSBT send event.
965967
func testMultiInputPsbtSingleAssetID(t *harnessTest) {
966968
var (
967969
ctxb = context.Background()
@@ -974,6 +976,12 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
974976
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
975977
)
976978
rpcAsset := rpcAssets[0]
979+
assetTotalAmtMinted := simpleAssets[0].Asset.Amount
980+
981+
// The primary node asset total should be equal to the amount minted.
982+
// This variable will serve as a running total of the amount of the
983+
// asset on the primary node.
984+
primaryTapdAssetAmt := assetTotalAmtMinted
977985

978986
// Set up a node that will serve as the final multi input PSBT sender
979987
// node.
@@ -984,13 +992,16 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
984992
require.NoError(t.t, secondaryTapd.stop(!*noDelete))
985993
}()
986994

987-
// First of two send events from primary (minting) node to secondary
995+
// First of three send events from primary (minting) node to secondary
988996
// node.
997+
sendAmt := uint64(1000)
998+
changeAmt := primaryTapdAssetAmt - sendAmt
999+
9891000
genInfo := rpcAsset.AssetGenesis
9901001
addr, err := secondaryTapd.NewAddr(
9911002
ctxb, &taprpc.NewAddrRequest{
9921003
AssetId: genInfo.AssetId,
993-
Amt: 1000,
1004+
Amt: sendAmt,
9941005
},
9951006
)
9961007
require.NoError(t.t, err)
@@ -1001,17 +1012,23 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10011012

10021013
ConfirmAndAssertOutboundTransfer(
10031014
t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp,
1004-
genInfo.AssetId, []uint64{4000, 1000}, 0, 1,
1015+
genInfo.AssetId, []uint64{changeAmt, sendAmt}, 0, 1,
10051016
)
10061017

10071018
AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 1)
10081019

1009-
// Second of two send events from primary (minting) node to the
1010-
// secondary node.
1020+
primaryTapdAssetAmt -= sendAmt
1021+
1022+
// Second of three send events from primary (minting) node to secondary
1023+
// node.
1024+
sendAmt = uint64(1000)
1025+
changeAmt = primaryTapdAssetAmt - sendAmt
1026+
1027+
genInfo = rpcAsset.AssetGenesis
10111028
addr, err = secondaryTapd.NewAddr(
10121029
ctxb, &taprpc.NewAddrRequest{
10131030
AssetId: genInfo.AssetId,
1014-
Amt: 4000,
1031+
Amt: sendAmt,
10151032
},
10161033
)
10171034
require.NoError(t.t, err)
@@ -1022,13 +1039,47 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10221039

10231040
ConfirmAndAssertOutboundTransfer(
10241041
t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp,
1025-
genInfo.AssetId, []uint64{0, 4000}, 1, 2,
1042+
genInfo.AssetId, []uint64{changeAmt, sendAmt}, 1, 2,
10261043
)
10271044

10281045
AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 2)
10291046

1030-
t.Logf("Two separate send events complete, now attempting to send " +
1031-
"back the full amount in a single multi-input PSBT send event")
1047+
primaryTapdAssetAmt -= sendAmt
1048+
1049+
// Third of three send events from primary (minting) node to the
1050+
// secondary node.
1051+
sendAmt = uint64(3000)
1052+
changeAmt = primaryTapdAssetAmt - sendAmt
1053+
1054+
addr, err = secondaryTapd.NewAddr(
1055+
ctxb, &taprpc.NewAddrRequest{
1056+
AssetId: genInfo.AssetId,
1057+
Amt: sendAmt,
1058+
},
1059+
)
1060+
require.NoError(t.t, err)
1061+
AssertAddrCreated(t.t, secondaryTapd, rpcAsset, addr)
1062+
1063+
// Send the assets to the secondary node.
1064+
sendResp = sendAssetsToAddr(t, primaryTapd, addr)
1065+
1066+
ConfirmAndAssertOutboundTransfer(
1067+
t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp,
1068+
genInfo.AssetId, []uint64{changeAmt, sendAmt}, 2, 3,
1069+
)
1070+
1071+
AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 3)
1072+
1073+
primaryTapdAssetAmt -= sendAmt
1074+
1075+
// At this point, all three send events have completed. The primary
1076+
// node should have no assets and the secondary node should have three
1077+
// assets.
1078+
require.Equal(t.t, uint64(0), primaryTapdAssetAmt)
1079+
1080+
t.Logf("Three separate send events complete. Now attempting to send " +
1081+
"a partial amount in a single multi-input PSBT send event " +
1082+
"back to the primary node from the secondary node.")
10321083

10331084
// Ensure that the primary node has no assets before we begin.
10341085
primaryNodeAssets, err := primaryTapd.ListAssets(
@@ -1037,27 +1088,45 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10371088
require.NoError(t.t, err)
10381089
require.Empty(t.t, primaryNodeAssets.Assets)
10391090

1091+
// The secondary node should have three assets as a result of the
1092+
// previous three send events.
1093+
secondaryNodeAssets, err := secondaryTapd.ListAssets(
1094+
ctxb, &taprpc.ListAssetRequest{},
1095+
)
1096+
require.NoError(t.t, err)
1097+
require.Len(t.t, secondaryNodeAssets.Assets, 3)
1098+
10401099
// We need to derive two keys for the receiver node, one for the new
10411100
// script key and one for the internal key.
1042-
receiverScriptKey, receiverAnchorIntKeyDesc := deriveKeys(
1101+
primaryNodeScriptKey, primaryNodeAnchorIntKeyDesc := deriveKeys(
10431102
t.t, primaryTapd,
10441103
)
10451104

10461105
var assetId asset.ID
10471106
copy(assetId[:], genInfo.AssetId)
10481107

1049-
var (
1050-
chainParams = &address.RegressionNetTap
1051-
sendAmt = uint64(5000)
1052-
)
1108+
chainParams := &address.RegressionNetTap
1109+
sendAmt = uint64(3500)
1110+
changeAmt = uint64(500)
10531111

10541112
vPkt := tappsbt.ForInteractiveSend(
1055-
assetId, sendAmt, receiverScriptKey, 0,
1056-
receiverAnchorIntKeyDesc, asset.V0, chainParams,
1113+
assetId, sendAmt, primaryNodeScriptKey, 0,
1114+
primaryNodeAnchorIntKeyDesc, asset.V0, chainParams,
10571115
)
10581116

10591117
// Next, we'll attempt to fund the PSBT.
10601118
fundResp := fundPacket(t, secondaryTapd, vPkt)
1119+
1120+
// Decode and inspect the funded vPSBT.
1121+
fundedVPsbtCopy := make([]byte, len(fundResp.FundedPsbt))
1122+
copy(fundedVPsbtCopy, fundResp.FundedPsbt)
1123+
bytesReader := bytes.NewReader(fundedVPsbtCopy)
1124+
1125+
vPkt, err = tappsbt.NewFromRawBytes(bytesReader, false)
1126+
require.NoError(t.t, err)
1127+
require.Equal(t.t, 2, len(vPkt.Inputs))
1128+
1129+
// Sign the funded vPSBT.
10611130
signResp, err := secondaryTapd.SignVirtualPsbt(
10621131
ctxb, &wrpc.SignVirtualPsbtRequest{
10631132
FundedPsbt: fundResp.FundedPsbt,
@@ -1077,20 +1146,20 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10771146
var (
10781147
currentTransferIdx = 0
10791148
numTransfers = 1
1080-
numOutputs = 1
1149+
numOutputs = 2
10811150
)
10821151
ConfirmAndAssertOutboundTransferWithOutputs(
10831152
t.t, t.lndHarness.Miner.Client, secondaryTapd,
10841153
sendResp, genInfo.AssetId,
1085-
[]uint64{sendAmt}, currentTransferIdx, numTransfers,
1154+
[]uint64{sendAmt, changeAmt}, currentTransferIdx, numTransfers,
10861155
numOutputs,
10871156
)
10881157

10891158
// This is an interactive transfer. Therefore, we will manually transfer
10901159
// the proof from the sender to the receiver.
10911160
_ = sendProof(
10921161
t, secondaryTapd, primaryTapd,
1093-
receiverScriptKey.PubKey.SerializeCompressed(), genInfo,
1162+
primaryNodeScriptKey.PubKey.SerializeCompressed(), genInfo,
10941163
)
10951164

10961165
// Finally, we make sure that the primary node has the asset.
@@ -1108,6 +1177,92 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
11081177
var foundAssetId asset.ID
11091178
copy(foundAssetId[:], primaryNodeAsset.AssetGenesis.AssetId)
11101179
require.Equal(t.t, assetId, foundAssetId)
1180+
1181+
t.Logf("Partial amount multi-input PSBT send event complete. Now " +
1182+
"attempting to send the remaining amount in a full value " +
1183+
"multi-input PSBT send event back to the primary node from " +
1184+
"the secondary node.")
1185+
1186+
// Attempt a full value send of the rest of the asset back to the
1187+
// primary node.
1188+
sendAmt = uint64(1500)
1189+
1190+
vPkt = tappsbt.ForInteractiveSend(
1191+
assetId, sendAmt, primaryNodeScriptKey, 0,
1192+
primaryNodeAnchorIntKeyDesc, asset.V0, chainParams,
1193+
)
1194+
1195+
// Next, we'll attempt to fund the PSBT.
1196+
fundResp = fundPacket(t, secondaryTapd, vPkt)
1197+
1198+
// Decode and inspect the funded vPSBT.
1199+
fundedVPsbtCopy = make([]byte, len(fundResp.FundedPsbt))
1200+
copy(fundedVPsbtCopy, fundResp.FundedPsbt)
1201+
bytesReader = bytes.NewReader(fundedVPsbtCopy)
1202+
1203+
vPkt, err = tappsbt.NewFromRawBytes(bytesReader, false)
1204+
require.NoError(t.t, err)
1205+
require.Equal(t.t, 2, len(vPkt.Inputs))
1206+
1207+
// Sign the funded vPSBT.
1208+
signResp, err = secondaryTapd.SignVirtualPsbt(
1209+
ctxb, &wrpc.SignVirtualPsbtRequest{
1210+
FundedPsbt: fundResp.FundedPsbt,
1211+
},
1212+
)
1213+
require.NoError(t.t, err)
1214+
1215+
// And finally anchor the PSBT in the BTC chain to complete the
1216+
// transfer.
1217+
sendResp, err = secondaryTapd.AnchorVirtualPsbts(
1218+
ctxb, &wrpc.AnchorVirtualPsbtsRequest{
1219+
VirtualPsbts: [][]byte{signResp.SignedPsbt},
1220+
},
1221+
)
1222+
require.NoError(t.t, err)
1223+
1224+
currentTransferIdx = 1
1225+
numTransfers = 2
1226+
numOutputs = 1
1227+
ConfirmAndAssertOutboundTransferWithOutputs(
1228+
t.t, t.lndHarness.Miner.Client, secondaryTapd, sendResp,
1229+
genInfo.AssetId, []uint64{sendAmt}, currentTransferIdx,
1230+
numTransfers, numOutputs,
1231+
)
1232+
1233+
// This is an interactive transfer. Therefore, we will manually transfer
1234+
// the proof from the sender to the receiver.
1235+
_ = sendProof(
1236+
t, secondaryTapd, primaryTapd,
1237+
primaryNodeScriptKey.PubKey.SerializeCompressed(), genInfo,
1238+
)
1239+
1240+
// Finally, we make sure that the primary node has the asset.
1241+
primaryNodeAssets, err = primaryTapd.ListAssets(
1242+
ctxb, &taprpc.ListAssetRequest{},
1243+
)
1244+
require.NoError(t.t, err)
1245+
require.Len(t.t, primaryNodeAssets.Assets, 2)
1246+
1247+
primaryTapdAssetAmt = 0
1248+
for idx := range primaryNodeAssets.Assets {
1249+
a := primaryNodeAssets.Assets[idx]
1250+
1251+
// Ensure matching asset ID.
1252+
copy(foundAssetId[:], a.AssetGenesis.AssetId)
1253+
require.Equal(t.t, assetId, foundAssetId)
1254+
1255+
require.True(t.t, a.Amount == 1500 || a.Amount == 3500)
1256+
primaryTapdAssetAmt += a.Amount
1257+
}
1258+
require.Equal(t.t, primaryTapdAssetAmt, assetTotalAmtMinted)
1259+
1260+
// Finally, we ensure that the secondary node has no assets.
1261+
secondaryNodeAssets, err = secondaryTapd.ListAssets(
1262+
ctxb, &taprpc.ListAssetRequest{},
1263+
)
1264+
require.NoError(t.t, err)
1265+
require.Len(t.t, secondaryNodeAssets.Assets, 0)
11111266
}
11121267

11131268
func deriveKeys(t *testing.T, tapd *tapdHarness) (asset.ScriptKey,

0 commit comments

Comments
 (0)