@@ -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.
0 commit comments