Skip to content

Commit 6f0f23f

Browse files
committed
itest: spend change on failed proof delivery
This commit adds an itest which ensures that a tapd node is able to spend a change output even if the proof transfer for the previous transaction fails.
1 parent 99daf3d commit 6f0f23f

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

itest/send_test.go

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,248 @@ func testReattemptFailedSendUniCourier(t *harnessTest) {
951951
wg.Wait()
952952
}
953953

954+
// testSpendChangeOutputWhenProofTransferFail tests that a tapd node is able
955+
// to spend a change output even if the proof transfer for the previous
956+
// transaction fails.
957+
func testSpendChangeOutputWhenProofTransferFail(t *harnessTest) {
958+
var (
959+
ctxb = context.Background()
960+
wg sync.WaitGroup
961+
)
962+
963+
// For this test we will use the universe server as the proof courier.
964+
proofCourier := t.universeServer
965+
966+
// Make a new tapd node which will send an asset to a receiving tapd
967+
// node.
968+
sendTapd := setupTapdHarness(
969+
t.t, t, t.lndHarness.Bob, t.universeServer,
970+
func(params *tapdHarnessParams) {
971+
params.expectErrExit = true
972+
params.proofCourier = proofCourier
973+
},
974+
)
975+
defer func() {
976+
// Any node that has been started within an itest should be
977+
// explicitly stopped within the same itest.
978+
require.NoError(t.t, sendTapd.stop(!*noDelete))
979+
}()
980+
981+
// Use the primary tapd node as the receiver node.
982+
recvTapd := t.tapd
983+
984+
// Use the sending node to mint an asset for sending.
985+
rpcAssets := MintAssetsConfirmBatch(
986+
t.t, t.lndHarness.Miner.Client, sendTapd,
987+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
988+
)
989+
990+
genInfo := rpcAssets[0].AssetGenesis
991+
992+
// After minting an asset with the sending node, we need to synchronize
993+
// the Universe state to ensure the receiving node is updated and aware
994+
// of the asset.
995+
t.syncUniverseState(sendTapd, recvTapd, len(rpcAssets))
996+
997+
// Create a new address for the receiver node. We will use the universe
998+
// server as the proof courier.
999+
proofCourierAddr := fmt.Sprintf(
1000+
"%s://%s", proof.UniverseRpcCourierType,
1001+
proofCourier.service.rpcHost(),
1002+
)
1003+
t.Logf("Proof courier address: %s", proofCourierAddr)
1004+
1005+
recvAddr, err := recvTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
1006+
AssetId: genInfo.AssetId,
1007+
Amt: 10,
1008+
ProofCourierAddr: proofCourierAddr,
1009+
})
1010+
require.NoError(t.t, err)
1011+
AssertAddrCreated(t.t, recvTapd, rpcAssets[0], recvAddr)
1012+
1013+
// Soon we will be attempting to send an asset to the receiver node. We
1014+
// want any associated proof delivery attempt to fail. Therefore, we
1015+
// will take the proof courier service offline.
1016+
t.Log("Stopping proof courier service")
1017+
require.NoError(t.t, proofCourier.Stop())
1018+
1019+
// Now that the proof courier service is offline, the sending node's
1020+
// attempt to transfer the asset proof should fail.
1021+
//
1022+
// We will soon start the asset transfer process. However, before we
1023+
// start, we subscribe to the send events from the sending tapd node so
1024+
// that we can be sure that a proof delivery has been attempted
1025+
// unsuccessfully. We assert that at least a single proof delivery
1026+
// attempt has been made by identifying a backoff wait event.
1027+
events := SubscribeSendEvents(t.t, sendTapd)
1028+
1029+
wg.Add(1)
1030+
go func() {
1031+
defer wg.Done()
1032+
1033+
// Define a target event selector to match the backoff wait
1034+
// event. This function selects for a specific event type.
1035+
targetEventSelector := func(
1036+
event *tapdevrpc.SendAssetEvent) bool {
1037+
1038+
return AssertSendEventProofTransferBackoffWaitTypeSend(
1039+
t, event,
1040+
)
1041+
}
1042+
1043+
// Set the context timeout for detecting a single proof delivery
1044+
// attempt to something reasonable.
1045+
timeout := 2 * defaultProofTransferReceiverAckTimeout
1046+
1047+
assertAssetNtfsEvent(
1048+
t, events, timeout, targetEventSelector, 1,
1049+
)
1050+
}()
1051+
1052+
// Start asset transfer and then mine to confirm the associated on-chain
1053+
// tx. The on-chain tx should be mined successfully, but we expect the
1054+
// asset proof transfer to be unsuccessful.
1055+
sendAssetsToAddr(t, sendTapd, recvAddr)
1056+
MineBlocks(t.t, t.lndHarness.Miner.Client, 1, 1)
1057+
1058+
// There may be a delay between mining the anchoring transaction and
1059+
// recognizing its on-chain confirmation. To handle this potential
1060+
// delay, we use require.Eventually to ensure the transfer details are
1061+
// correctly listed after confirmation.
1062+
require.Eventually(t.t, func() bool {
1063+
// Ensure that the transaction took place as expected.
1064+
listTransfersResp, err := sendTapd.ListTransfers(
1065+
ctxb, &taprpc.ListTransfersRequest{},
1066+
)
1067+
require.NoError(t.t, err)
1068+
1069+
require.Len(t.t, listTransfersResp.Transfers, 1)
1070+
1071+
firstTransfer := listTransfersResp.Transfers[0]
1072+
require.NotEqual(t.t, firstTransfer.AnchorTxHeightHint, 0)
1073+
require.NotEmpty(t.t, firstTransfer.AnchorTxBlockHash)
1074+
1075+
// Assert proof transfer status for each transfer output.
1076+
require.Len(t.t, firstTransfer.Outputs, 2)
1077+
1078+
// First output should have a proof delivery status of not
1079+
// applicable. This indicates that a proof will not be delivered
1080+
// for this output.
1081+
firstOutput := firstTransfer.Outputs[0]
1082+
require.Equal(
1083+
t.t, taprpc.ProofDeliveryStatusNotApplicable,
1084+
firstOutput.ProofDeliveryStatus,
1085+
)
1086+
1087+
// The second output should have a proof delivery status of
1088+
// pending. This indicates that the proof deliver has not yet
1089+
// completed successfully.
1090+
secondOutput := firstTransfer.Outputs[1]
1091+
require.Equal(
1092+
t.t, taprpc.ProofDeliveryStatusPending,
1093+
secondOutput.ProofDeliveryStatus,
1094+
)
1095+
1096+
return true
1097+
}, defaultWaitTimeout, 200*time.Millisecond)
1098+
1099+
// Wait to ensure that the asset transfer proof deliver attempt has been
1100+
// made.
1101+
wg.Wait()
1102+
1103+
// Attempt to send the change output to the receiver node. This
1104+
// operation should select the change output from the previous
1105+
// transaction and transmit it to the receiver node, despite the fact
1106+
// that proof delivery for the previous transaction remains incomplete
1107+
// (due to the proof courier being shut down). We will generate a new
1108+
// address for this new transaction.
1109+
recvAddr, err = recvTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
1110+
AssetId: genInfo.AssetId,
1111+
Amt: 42,
1112+
ProofCourierAddr: proofCourierAddr,
1113+
})
1114+
require.NoError(t.t, err)
1115+
AssertAddrCreated(t.t, recvTapd, rpcAssets[0], recvAddr)
1116+
1117+
sendAssetsToAddr(t, sendTapd, recvAddr)
1118+
MineBlocks(t.t, t.lndHarness.Miner.Client, 1, 1)
1119+
1120+
// There may be a delay between mining the anchoring transaction and
1121+
// recognizing its on-chain confirmation. To handle this potential
1122+
// delay, we use require.Eventually to ensure the transfer details are
1123+
// correctly listed after confirmation.
1124+
require.Eventually(t.t, func() bool {
1125+
// Ensure that the transaction took place as expected.
1126+
listTransfersResp, err := sendTapd.ListTransfers(
1127+
ctxb, &taprpc.ListTransfersRequest{},
1128+
)
1129+
require.NoError(t.t, err)
1130+
1131+
require.Len(t.t, listTransfersResp.Transfers, 2)
1132+
1133+
// Inspect the first transfer.
1134+
firstTransfer := listTransfersResp.Transfers[0]
1135+
require.NotEqual(t.t, firstTransfer.AnchorTxHeightHint, 0)
1136+
require.NotEmpty(t.t, firstTransfer.AnchorTxBlockHash)
1137+
1138+
// Assert proof transfer status for each transfer output.
1139+
require.Len(t.t, firstTransfer.Outputs, 2)
1140+
1141+
// First output should have a proof delivery status of not
1142+
// applicable. This indicates that a proof will not be delivered
1143+
// for this output.
1144+
firstOutput := firstTransfer.Outputs[0]
1145+
require.Equal(
1146+
t.t, taprpc.ProofDeliveryStatusNotApplicable,
1147+
firstOutput.ProofDeliveryStatus,
1148+
)
1149+
1150+
// The second output should have a proof delivery status of
1151+
// pending. This indicates that the proof deliver has not yet
1152+
// completed successfully.
1153+
secondOutput := firstTransfer.Outputs[1]
1154+
require.Equal(
1155+
t.t, taprpc.ProofDeliveryStatusPending,
1156+
secondOutput.ProofDeliveryStatus,
1157+
)
1158+
1159+
// Inspect the second transfer.
1160+
secondTransfer := listTransfersResp.Transfers[1]
1161+
require.NotEqual(t.t, secondTransfer.AnchorTxHeightHint, 0)
1162+
require.NotEmpty(t.t, secondTransfer.AnchorTxBlockHash)
1163+
1164+
// Assert proof transfer status for each transfer output.
1165+
require.Len(t.t, secondTransfer.Outputs, 2)
1166+
1167+
// First output should have a proof delivery status of not
1168+
// applicable. This indicates that a proof will not be delivered
1169+
// for this output.
1170+
firstOutput = secondTransfer.Outputs[0]
1171+
require.Equal(
1172+
t.t, taprpc.ProofDeliveryStatusNotApplicable,
1173+
firstOutput.ProofDeliveryStatus,
1174+
)
1175+
1176+
// The second output should have a proof delivery status of
1177+
// pending. This indicates that the proof deliver has not yet
1178+
// completed successfully.
1179+
secondOutput = secondTransfer.Outputs[1]
1180+
require.Equal(
1181+
t.t, taprpc.ProofDeliveryStatusPending,
1182+
secondOutput.ProofDeliveryStatus,
1183+
)
1184+
1185+
return true
1186+
}, defaultWaitTimeout, 200*time.Millisecond)
1187+
1188+
// Restart the proof courier service.
1189+
t.Log("Starting proof courier service")
1190+
require.NoError(t.t, proofCourier.Start(nil))
1191+
1192+
// TODO(ffranr): Assert proof transfer complete after proof courier
1193+
// restart.
1194+
}
1195+
9541196
// testReattemptFailedReceiveUniCourier ensures that a failed attempt to receive
9551197
// an asset proof is retried by the receiving Tapd node. This test focuses on
9561198
// the universe proof courier.

itest/test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ var testCases = []*testCase{
9696
name: "reattempt proof transfer on tapd restart",
9797
test: testReattemptProofTransferOnTapdRestart,
9898
},
99+
{
100+
name: "spend change output when proof transfer fail",
101+
test: testSpendChangeOutputWhenProofTransferFail,
102+
},
99103
{
100104
name: "reattempt failed receive uni courier",
101105
test: testReattemptFailedReceiveUniCourier,

0 commit comments

Comments
 (0)