Skip to content

Commit 332a550

Browse files
committed
itest: ensure that undelivered transfer proofs are delivered on restart
This commit adds a test which ensures that a failed attempt at transferring a transfer output proof to a proof courier will be reattempted by the sending tapd node upon restart.
1 parent 1cf9fc4 commit 332a550

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

itest/send_test.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
1717
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
1818
"github.com/lightninglabs/taproot-assets/taprpc/tapdevrpc"
19+
unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc"
1920
"github.com/lightningnetwork/lnd/lntest/wait"
2021
"github.com/stretchr/testify/require"
2122
)
@@ -664,6 +665,194 @@ func testReattemptFailedSendHashmailCourier(t *harnessTest) {
664665
wg.Wait()
665666
}
666667

668+
// testReattemptProofTransferOnTapdRestart tests that a failed attempt at
669+
// transferring a transfer output proof to a proof courier will be reattempted
670+
// by the sending tapd node upon restart. This test targets the universe
671+
// courier.
672+
func testReattemptProofTransferOnTapdRestart(t *harnessTest) {
673+
var (
674+
ctxb = context.Background()
675+
wg sync.WaitGroup
676+
)
677+
678+
// For this test we will use the universe server as the proof courier.
679+
proofCourier := t.universeServer
680+
681+
// Make a new tapd node which will send an asset to a receiving tapd
682+
// node.
683+
sendTapd := setupTapdHarness(
684+
t.t, t, t.lndHarness.Bob, t.universeServer,
685+
func(params *tapdHarnessParams) {
686+
params.expectErrExit = true
687+
params.proofCourier = proofCourier
688+
},
689+
)
690+
defer func() {
691+
// Any node that has been started within an itest should be
692+
// explicitly stopped within the same itest.
693+
require.NoError(t.t, sendTapd.stop(!*noDelete))
694+
}()
695+
696+
// Use the primary tapd node as the receiver node.
697+
recvTapd := t.tapd
698+
699+
// Use the sending node to mint an asset for sending.
700+
rpcAssets := MintAssetsConfirmBatch(
701+
t.t, t.lndHarness.Miner.Client, sendTapd,
702+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
703+
)
704+
705+
genInfo := rpcAssets[0].AssetGenesis
706+
707+
// After minting an asset with the sending node, we need to synchronize
708+
// the Universe state to ensure the receiving node is updated and aware
709+
// of the asset.
710+
t.syncUniverseState(sendTapd, recvTapd, len(rpcAssets))
711+
712+
// Create a new address for the receiver node. We will use the universe
713+
// server as the proof courier.
714+
proofCourierAddr := fmt.Sprintf(
715+
"%s://%s", proof.UniverseRpcCourierType,
716+
proofCourier.service.rpcHost(),
717+
)
718+
t.Logf("Proof courier address: %s", proofCourierAddr)
719+
720+
recvAddr, err := recvTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
721+
AssetId: genInfo.AssetId,
722+
Amt: 10,
723+
ProofCourierAddr: proofCourierAddr,
724+
})
725+
require.NoError(t.t, err)
726+
AssertAddrCreated(t.t, recvTapd, rpcAssets[0], recvAddr)
727+
728+
// Soon we will be attempting to send an asset to the receiver node. We
729+
// want the attempt to fail until we restart the sending node.
730+
// Therefore, we will take the proof courier service offline.
731+
t.Log("Stopping proof courier service")
732+
require.NoError(t.t, proofCourier.Stop())
733+
734+
// Now that the proof courier service is offline, the sending node's
735+
// attempt to transfer the asset proof should fail.
736+
//
737+
// We will soon start the asset transfer process. However, before we
738+
// start, we subscribe to the send events from the sending tapd node so
739+
// that we can be sure that a transfer has been attempted.
740+
events := SubscribeSendEvents(t.t, sendTapd)
741+
742+
wg.Add(1)
743+
go func() {
744+
defer wg.Done()
745+
746+
// Define a target event selector to match the backoff wait
747+
// event. This function selects for a specific event type.
748+
targetEventSelector := func(
749+
event *tapdevrpc.SendAssetEvent) bool {
750+
751+
return AssertSendEventProofTransferBackoffWaitTypeSend(
752+
t, event,
753+
)
754+
}
755+
756+
// Expected number of events is one less than the number of
757+
// tries because the first attempt does not count as a backoff
758+
// event.
759+
nodeBackoffCfg :=
760+
sendTapd.clientCfg.UniverseRpcCourier.BackoffCfg
761+
expectedEventCount := nodeBackoffCfg.NumTries - 1
762+
763+
// Context timeout scales with expected number of events.
764+
timeout := time.Duration(expectedEventCount) *
765+
defaultProofTransferReceiverAckTimeout
766+
767+
// Allow for some margin for the operations that aren't pure
768+
// waiting on the receiver ACK.
769+
timeout += timeoutMargin
770+
771+
assertAssetNtfsEvent(
772+
t, events, timeout, targetEventSelector,
773+
expectedEventCount,
774+
)
775+
}()
776+
777+
// Start asset transfer and then mine to confirm the associated on-chain
778+
// tx. The on-chain tx should be mined successfully, but we expect the
779+
// asset proof transfer to be unsuccessful.
780+
sendResp, _ := sendAssetsToAddr(t, sendTapd, recvAddr)
781+
MineBlocks(t.t, t.lndHarness.Miner.Client, 1, 1)
782+
783+
// Wait to ensure that the asset transfer attempt has been made.
784+
wg.Wait()
785+
786+
// Stop the sending tapd node. This downtime will give us the
787+
// opportunity to restart the proof courier service.
788+
t.Log("Stopping sending tapd node")
789+
require.NoError(t.t, sendTapd.stop(false))
790+
791+
// Restart the proof courier service.
792+
t.Log("Starting proof courier service")
793+
require.NoError(t.t, proofCourier.Start(nil))
794+
t.Logf("Proof courier address: %s", proofCourier.service.rpcHost())
795+
796+
// Ensure that the proof courier address has not changed on restart.
797+
// The port is currently selected opportunistically.
798+
// If the proof courier address has changed the tap address will be
799+
// stale.
800+
newProofCourierAddr := fmt.Sprintf(
801+
"%s://%s", proof.UniverseRpcCourierType,
802+
proofCourier.service.rpcHost(),
803+
)
804+
require.Equal(t.t, proofCourierAddr, newProofCourierAddr)
805+
806+
// Identify receiver's asset transfer output.
807+
require.Len(t.t, sendResp.Transfer.Outputs, 2)
808+
recvOutput := sendResp.Transfer.Outputs[0]
809+
810+
// If the script key of the output is local to the sending node, then
811+
// the receiver's output is the second output.
812+
if recvOutput.ScriptKeyIsLocal {
813+
recvOutput = sendResp.Transfer.Outputs[1]
814+
}
815+
816+
// Formulate a universe key to query the proof courier for the asset
817+
// transfer proof.
818+
uniKey := unirpc.UniverseKey{
819+
Id: &unirpc.ID{
820+
Id: &unirpc.ID_AssetId{
821+
AssetId: genInfo.AssetId,
822+
},
823+
ProofType: unirpc.ProofType_PROOF_TYPE_TRANSFER,
824+
},
825+
LeafKey: &unirpc.AssetKey{
826+
Outpoint: &unirpc.AssetKey_OpStr{
827+
OpStr: recvOutput.Anchor.Outpoint,
828+
},
829+
ScriptKey: &unirpc.AssetKey_ScriptKeyBytes{
830+
ScriptKeyBytes: recvOutput.ScriptKey,
831+
},
832+
},
833+
}
834+
835+
// Ensure that the transfer proof has not reached the proof courier yet.
836+
resp, err := proofCourier.service.QueryProof(ctxb, &uniKey)
837+
require.Nil(t.t, resp)
838+
require.ErrorContains(t.t, err, "no universe proof found")
839+
840+
// Restart the sending tapd node. The node should reattempt to transfer
841+
// the asset proof to the proof courier.
842+
t.Log("Restarting sending tapd node")
843+
require.NoError(t.t, sendTapd.start(false))
844+
845+
require.Eventually(t.t, func() bool {
846+
resp, err = proofCourier.service.QueryProof(ctxb, &uniKey)
847+
return err == nil && resp != nil
848+
}, defaultWaitTimeout, 200*time.Millisecond)
849+
850+
// TODO(ffranr): Modify the receiver node proof retrieval backoff
851+
// schedule such that we can assert that the transfer fully completes
852+
// in a timely and predictable manner.
853+
// AssertNonInteractiveRecvComplete(t.t, recvTapd, 1)
854+
}
855+
667856
// testReattemptFailedSendUniCourier tests that a failed attempt at
668857
// sending an asset proof will be reattempted by the tapd node. This test
669858
// targets the universe proof courier.

itest/test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ var testCases = []*testCase{
9292
name: "reattempt failed send uni courier",
9393
test: testReattemptFailedSendUniCourier,
9494
},
95+
{
96+
name: "reattempt proof transfer on tapd restart",
97+
test: testReattemptProofTransferOnTapdRestart,
98+
},
9599
{
96100
name: "reattempt failed receive uni courier",
97101
test: testReattemptFailedReceiveUniCourier,

0 commit comments

Comments
 (0)