diff --git a/itest/addrs_test.go b/itest/addrs_test.go index 6fa71950b..60d2669ef 100644 --- a/itest/addrs_test.go +++ b/itest/addrs_test.go @@ -10,6 +10,7 @@ import ( "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/internal/test" "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/taprpc" wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" @@ -51,17 +52,18 @@ func testAddresses(t *harnessTest) { for idx, a := range rpcAssets { // In order to force a split, we don't try to send the full // asset. - addr, err := secondTapd.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: a.AssetGenesis.AssetId, - Amt: a.Amount - 1, - AssetVersion: a.Version, - }) - require.NoError(t.t, err) + addr, events := NewAddrWithEventStream( + t.t, secondTapd, &taprpc.NewAddrRequest{ + AssetId: a.AssetGenesis.AssetId, + Amt: a.Amount - 1, + AssetVersion: a.Version, + }, + ) addresses = append(addresses, addr) AssertAddrCreated(t.t, secondTapd, a, addr) - sendResp := sendAssetsToAddr(t, t.tapd, addr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, addr) sendRespJSON, err := formatProtoJSON(sendResp) require.NoError(t.t, err) @@ -79,6 +81,15 @@ func testAddresses(t *harnessTest) { // Make sure we have imported and finalized all proofs. AssertNonInteractiveRecvComplete(t.t, secondTapd, idx+1) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + + // Make sure the receiver has received all events in order for + // the address. + AssertReceiveEvents(t.t, addr, events) // Make sure the asset meta is also fetched correctly. assetResp, err := secondTapd.FetchAssetMeta( @@ -401,7 +412,7 @@ func testAddressAssetSyncer(t *harnessTest) { // group lookups by Bob. AssertUniverseStats(t.t, t.tapd, 4, 4, 2) - // If Alice now mints a reissuance for the second asset group, Bob + // If Alice now mints a re-issuance for the second asset group, Bob // should successfully sync that new asset. secondGroupMember := CopyRequest(issuableAssets[1]) secondGroupMember.Asset.NewGroupedAsset = false @@ -430,37 +441,40 @@ func runMultiSendTest(ctxt context.Context, t *harnessTest, alice, // In order to force a split, we don't try to send the full asset. const sendAmt = 100 - bobAddr1, err := bob.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: genInfo.AssetId, - Amt: sendAmt, - }) - require.NoError(t.t, err) + bobAddr1, bobEvents1 := NewAddrWithEventStream( + t.t, bob, &taprpc.NewAddrRequest{ + AssetId: genInfo.AssetId, + Amt: sendAmt, + }, + ) AssertAddrCreated(t.t, bob, mintedAsset, bobAddr1) - bobAddr2, err := bob.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: genInfo.AssetId, - Amt: sendAmt, - }) - require.NoError(t.t, err) + bobAddr2, bobEvents2 := NewAddrWithEventStream( + t.t, bob, &taprpc.NewAddrRequest{ + AssetId: genInfo.AssetId, + Amt: sendAmt, + }) AssertAddrCreated(t.t, bob, mintedAsset, bobAddr2) // To test that Alice can also receive to multiple addresses in a single // transaction as well, we also add two addresses for her. - aliceAddr1, err := alice.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: genInfo.AssetId, - Amt: sendAmt, - }) - require.NoError(t.t, err) + aliceAddr1, aliceEvents1 := NewAddrWithEventStream( + t.t, alice, &taprpc.NewAddrRequest{ + AssetId: genInfo.AssetId, + Amt: sendAmt, + }, + ) AssertAddrCreated(t.t, alice, mintedAsset, aliceAddr1) - aliceAddr2, err := alice.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: genInfo.AssetId, - Amt: sendAmt, - }) - require.NoError(t.t, err) + aliceAddr2, aliceEvents2 := NewAddrWithEventStream( + t.t, alice, &taprpc.NewAddrRequest{ + AssetId: genInfo.AssetId, + Amt: sendAmt, + }, + ) AssertAddrCreated(t.t, alice, mintedAsset, aliceAddr2) - sendResp := sendAssetsToAddr( + sendResp, sendEvents := sendAssetsToAddr( t, alice, bobAddr1, bobAddr2, aliceAddr1, aliceAddr2, ) sendRespJSON, err := formatProtoJSON(sendResp) @@ -486,6 +500,18 @@ func runMultiSendTest(ctxt context.Context, t *harnessTest, alice, // Make sure we have imported and finalized all proofs. AssertNonInteractiveRecvComplete(t.t, bob, numRuns*2) AssertNonInteractiveRecvComplete(t.t, alice, numRuns*2) + AssertSendEvents( + t.t, bobAddr1.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + + // Make sure the receivers have received all events in order for the + //addresses. + AssertReceiveEvents(t.t, bobAddr1, bobEvents1) + AssertReceiveEvents(t.t, bobAddr2, bobEvents2) + AssertReceiveEvents(t.t, aliceAddr1, aliceEvents1) + AssertReceiveEvents(t.t, aliceAddr2, aliceEvents2) // Now sanity check that we can actually list the transfer. const ( @@ -647,23 +673,47 @@ func sendProofUniRPC(t *harnessTest, src, dst *tapdHarness, scriptKey []byte, // sendAssetsToAddr spends the given input asset and sends the amount specified // in the address to the Taproot output derived from the address. func sendAssetsToAddr(t *harnessTest, sender *tapdHarness, - receiverAddrs ...*taprpc.Addr) *taprpc.SendAssetResponse { + receiverAddrs ...*taprpc.Addr) (*taprpc.SendAssetResponse, + *EventSubscription[*taprpc.SendEvent]) { ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) defer cancel() + require.NotEmpty(t.t, receiverAddrs) + scriptKey := receiverAddrs[0].ScriptKey + encodedAddrs := make([]string, len(receiverAddrs)) for i, addr := range receiverAddrs { encodedAddrs[i] = addr.Encoded } + ctxc, streamCancel := context.WithCancel(ctxb) + stream, err := sender.SubscribeSendEvents( + ctxc, &taprpc.SubscribeSendEventsRequest{ + FilterScriptKey: scriptKey, + }, + ) + require.NoError(t.t, err) + sub := &EventSubscription[*taprpc.SendEvent]{ + ClientEventStream: stream, + Cancel: streamCancel, + } + resp, err := sender.SendAsset(ctxt, &taprpc.SendAssetRequest{ TapAddrs: encodedAddrs, }) require.NoError(t.t, err) - return resp + // Since we're filtering by script key, we'll only start getting events + // from the virtual sign state. And since we'll need to confirm the + // block, we'll only get events until the wait tx conf state. + AssertSendEvents( + t.t, scriptKey, sub, tapfreighter.SendStateVirtualSign, + tapfreighter.SendStateWaitTxConf, + ) + + return resp, sub } // fundAddressSendPacket asks the wallet to fund a new virtual packet with the diff --git a/itest/assertions.go b/itest/assertions.go index 98abd825e..06a76f005 100644 --- a/itest/assertions.go +++ b/itest/assertions.go @@ -20,6 +20,8 @@ import ( "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapfreighter" + "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/taprpc" wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" @@ -31,11 +33,13 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" + "google.golang.org/protobuf/proto" ) var ( statusDetected = taprpc.AddrEventStatus_ADDR_EVENT_STATUS_TRANSACTION_DETECTED statusConfirmed = taprpc.AddrEventStatus_ADDR_EVENT_STATUS_TRANSACTION_CONFIRMED + proofReceived = taprpc.AddrEventStatus_ADDR_EVENT_STATUS_PROOF_RECEIVED statusCompleted = taprpc.AddrEventStatus_ADDR_EVENT_STATUS_COMPLETED ) @@ -766,6 +770,114 @@ func AssertAddrEventByStatus(t *testing.T, client taprpc.TaprootAssetsClient, require.NoError(t, err) } +// AssertReceiveEvents makes sure all events with incremental status are sent +// on the stream for the given address. +func AssertReceiveEvents(t *testing.T, addr *taprpc.Addr, + stream *EventSubscription[*taprpc.ReceiveEvent]) { + + success := make(chan struct{}) + timeout := time.After(defaultWaitTimeout) + startStatus := statusDetected + + // To make sure we don't forever hang on receiving on the stream, we'll + // cancel it after the timeout. + go func() { + select { + case <-timeout: + stream.Cancel() + + case <-success: + } + }() + + for { + event, err := stream.Recv() + require.NoError(t, err, "receiving address receive event") + + require.True(t, proto.Equal(event.Address, addr)) + require.Equal(t, startStatus, event.Status) + + if event.Status == statusCompleted { + close(success) + stream.Cancel() + + return + } + + startStatus++ + } +} + +// AssertSendEvents makes sure all events with incremental status are sent +// on the stream for the given script key. +func AssertSendEvents(t *testing.T, scriptKey []byte, + stream *EventSubscription[*taprpc.SendEvent], from, + to tapfreighter.SendState) { + + success := make(chan struct{}) + timeout := time.After(defaultWaitTimeout) + startStatus := from + + targetScriptKey, err := btcec.ParsePubKey(scriptKey) + require.NoError(t, err) + + sendsToKey := func(e *taprpc.SendEvent) bool { + for _, vPacketBytes := range e.VirtualPackets { + vPkt, err := tappsbt.Decode(vPacketBytes) + require.NoError(t, err) + + for _, vOut := range vPkt.Outputs { + if vOut.ScriptKey.PubKey == nil { + continue + } + + // This packet sends to the filtered script key, + // we want to include this event. + if vOut.ScriptKey.PubKey.IsEqual( + targetScriptKey, + ) { + + return true + } + } + } + + return false + } + + // To make sure we don't forever hang on receiving on the stream, we'll + // cancel it after the timeout. + go func() { + select { + case <-timeout: + stream.Cancel() + + case <-success: + } + }() + + for { + event, err := stream.Recv() + require.NoError(t, err, "receiving send event") + + require.True(t, sendsToKey(event)) + require.Equal(t, startStatus.String(), event.SendState) + + // Fully close the stream once we definitely no longer need the + // stream. + if event.SendState == tapfreighter.SendStateComplete.String() { + stream.Cancel() + } + + if event.SendState == to.String() { + close(success) + return + } + + startStatus++ + } +} + // ConfirmAndAssertOutboundTransfer makes sure the given outbound transfer has // the correct state before confirming it and then asserting the confirmed state // with the node. diff --git a/itest/burn_test.go b/itest/burn_test.go index e6ce8bac4..57350404f 100644 --- a/itest/burn_test.go +++ b/itest/burn_test.go @@ -173,19 +173,22 @@ func testBurnAssets(t *harnessTest) { // the anchor output it was in (together with the change). So let's test // that we can successfully spend the change output. secondSendAmt := outputAmounts[2] - burnAmt - fullSendAddr, err := t.tapd.NewAddr(ctxt, &taprpc.NewAddrRequest{ - AssetId: simpleAssetGen.AssetId, - Amt: secondSendAmt, - }) + fullSendAddr, stream := NewAddrWithEventStream( + t.t, t.tapd, &taprpc.NewAddrRequest{ + AssetId: simpleAssetGen.AssetId, + Amt: secondSendAmt, + }, + ) require.NoError(t.t, err) AssertAddrCreated(t.t, t.tapd, simpleAsset, fullSendAddr) - sendResp = sendAssetsToAddr(t, t.tapd, fullSendAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, fullSendAddr) ConfirmAndAssertOutboundTransfer( t.t, minerClient, t.tapd, sendResp, simpleAssetGen.AssetId, []uint64{0, secondSendAmt}, 2, 3, ) AssertNonInteractiveRecvComplete(t.t, t.tapd, 1) + AssertReceiveEvents(t.t, fullSendAddr, stream) // Test case 3: Burn all assets of one asset ID (in this case a single // collectible from the original mint TX), while there are other, @@ -207,6 +210,11 @@ func testBurnAssets(t *harnessTest) { t.t, minerClient, t.tapd, burnResp.BurnTransfer, simpleCollectibleGen.AssetId, []uint64{1}, 3, 4, 1, true, ) + AssertSendEvents( + t.t, fullSendAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Test case 4: Burn assets from multiple inputs. This will select the // two largest inputs we have, the one over 1500 we sent above and the diff --git a/itest/collectible_split_test.go b/itest/collectible_split_test.go index 2f987e8e7..71efec4a4 100644 --- a/itest/collectible_split_test.go +++ b/itest/collectible_split_test.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc" @@ -65,8 +66,6 @@ func testCollectibleSend(t *harnessTest) { senderTransferIdx = 0 receiverTransferIdx = 0 fullAmount = rpcAssets[0].Amount - receiverAddr *taprpc.Addr - err error ) for i := 0; i < numSends; i++ { @@ -74,18 +73,19 @@ func testCollectibleSend(t *harnessTest) { // start with Bob receiving the asset, then sending it back // to the main node, and so on. if i%2 == 0 { - receiverAddr, err = secondTapd.NewAddr( - ctxb, &taprpc.NewAddrRequest{ + receiverAddr, events := NewAddrWithEventStream( + t.t, secondTapd, &taprpc.NewAddrRequest{ AssetId: genInfo.AssetId, Amt: fullAmount, }, ) - require.NoError(t.t, err) AssertAddrCreated( t.t, secondTapd, rpcAssets[0], receiverAddr, ) - sendResp := sendAssetsToAddr(t, t.tapd, receiverAddr) + sendResp, sendEvents := sendAssetsToAddr( + t, t.tapd, receiverAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, genInfo.AssetId, @@ -97,19 +97,24 @@ func testCollectibleSend(t *harnessTest) { AssertNonInteractiveRecvComplete( t.t, secondTapd, senderTransferIdx, ) + AssertSendEvents( + t.t, receiverAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + AssertReceiveEvents(t.t, receiverAddr, events) } else { - receiverAddr, err = t.tapd.NewAddr( - ctxb, &taprpc.NewAddrRequest{ + receiverAddr, events := NewAddrWithEventStream( + t.t, t.tapd, &taprpc.NewAddrRequest{ AssetId: genInfo.AssetId, Amt: fullAmount, }, ) - require.NoError(t.t, err) AssertAddrCreated( t.t, t.tapd, rpcAssets[0], receiverAddr, ) - sendResp := sendAssetsToAddr( + sendResp, sendEvents := sendAssetsToAddr( t, secondTapd, receiverAddr, ) ConfirmAndAssertOutboundTransfer( @@ -122,6 +127,12 @@ func testCollectibleSend(t *harnessTest) { AssertNonInteractiveRecvComplete( t.t, t.tapd, receiverTransferIdx, ) + AssertSendEvents( + t.t, receiverAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + AssertReceiveEvents(t.t, receiverAddr, events) } } @@ -188,7 +199,7 @@ func testCollectibleSend(t *harnessTest) { require.NoError(t.t, err) AssertAddrCreated(t.t, secondTapd, rpcAssets[1], bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, bobAddr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, passiveGen.AssetId, []uint64{0, rpcAssets[1].Amount}, 2, 3, @@ -196,6 +207,11 @@ func testCollectibleSend(t *harnessTest) { // There's only one non-interactive receive event. AssertNonInteractiveRecvComplete(t.t, secondTapd, 3) + AssertSendEvents( + t.t, bobAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } // testCollectibleGroupSend tests that we can properly send a collectible asset @@ -394,14 +410,16 @@ func sendAssets(t *testing.T, ctx context.Context, numAssets uint64, // Let's create an address on the receiving node and make sure it's // created correctly. - addr, err := receive.NewAddr(ctx, &taprpc.NewAddrRequest{ - AssetId: sendAsset.AssetGenesis.AssetId, - Amt: numAssets, - ProofCourierAddr: fmt.Sprintf( - "%s://%s", proof.UniverseRpcCourierType, send.rpcHost(), - ), - }) - require.NoError(t, err) + addr, stream := NewAddrWithEventStream( + t, receive, &taprpc.NewAddrRequest{ + AssetId: sendAsset.AssetGenesis.AssetId, + Amt: numAssets, + ProofCourierAddr: fmt.Sprintf( + "%s://%s", proof.UniverseRpcCourierType, + send.rpcHost(), + ), + }, + ) AssertAddrCreated(t, receive, sendAsset, addr) // Before we send the asset, we record the existing transfers on the @@ -410,7 +428,7 @@ func sendAssets(t *testing.T, ctx context.Context, numAssets uint64, transfersBefore := send.listTransfersSince(t, ctx, nil) // Initiate the send now. - _, err = send.SendAsset(ctx, &taprpc.SendAssetRequest{ + _, err := send.SendAsset(ctx, &taprpc.SendAssetRequest{ TapAddrs: []string{addr.Encoded}, }) require.NoError(t, err) @@ -429,6 +447,7 @@ func sendAssets(t *testing.T, ctx context.Context, numAssets uint64, // Now the transfer should go to completed eventually. AssertAddrEvent(t, receive, addr, 1, statusCompleted) + AssertReceiveEvents(t, addr, stream) } // pickSendNode picks a node at random, checks whether it has enough assets of diff --git a/itest/fee_estimation_test.go b/itest/fee_estimation_test.go index 78af11005..bc0acc99c 100644 --- a/itest/fee_estimation_test.go +++ b/itest/fee_estimation_test.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -79,16 +80,15 @@ func testFeeEstimation(t *harnessTest) { // Split the normal asset to create a transfer with two anchor outputs. normalAssetId := rpcAssets[0].AssetGenesis.AssetId splitAmount := rpcAssets[0].Amount / 2 - addr, err := t.tapd.NewAddr( - ctxt, &taprpc.NewAddrRequest{ + addr, stream := NewAddrWithEventStream( + t.t, t.tapd, &taprpc.NewAddrRequest{ AssetId: normalAssetId, Amt: splitAmount, }, ) - require.NoError(t.t, err) AssertAddrCreated(t.t, t.tapd, rpcAssets[0], addr) - sendResp := sendAssetsToAddr(t, t.tapd, addr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, addr) transferIdx := 0 ConfirmAndAssertOutboundTransfer( @@ -97,6 +97,12 @@ func testFeeEstimation(t *harnessTest) { ) transferIdx += 1 AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + AssertReceiveEvents(t.t, addr, stream) sendInputAmt := anchorAmounts[1] + 1000 AssertTransferFeeRate( @@ -108,16 +114,15 @@ func testFeeEstimation(t *harnessTest) { t.lndHarness.SetFeeEstimateWithConf(higherFeeRate, 6) secondSplitAmount := splitAmount / 2 - addr2, err := t.tapd.NewAddr( - ctxt, &taprpc.NewAddrRequest{ + addr2, stream2 := NewAddrWithEventStream( + t.t, t.tapd, &taprpc.NewAddrRequest{ AssetId: normalAssetId, Amt: secondSplitAmount, }, ) - require.NoError(t.t, err) AssertAddrCreated(t.t, t.tapd, rpcAssets[0], addr2) - sendResp = sendAssetsToAddr(t, t.tapd, addr2) + sendResp, sendEvents = sendAssetsToAddr(t, t.tapd, addr2) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, normalAssetId, @@ -126,6 +131,12 @@ func testFeeEstimation(t *harnessTest) { ) transferIdx += 1 AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) + AssertSendEvents( + t.t, addr2.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + AssertReceiveEvents(t.t, addr2, stream2) sendInputAmt = anchorAmounts[2] + 1000 AssertTransferFeeRate( @@ -138,13 +149,12 @@ func testFeeEstimation(t *harnessTest) { t.lndHarness.SetFeeEstimateWithConf(excessiveFeeRate, 6) thirdSplitAmount := splitAmount / 4 - addr3, err := t.tapd.NewAddr( - ctxt, &taprpc.NewAddrRequest{ + addr3, stream3 := NewAddrWithEventStream( + t.t, t.tapd, &taprpc.NewAddrRequest{ AssetId: normalAssetId, Amt: thirdSplitAmount, }, ) - require.NoError(t.t, err) AssertAddrCreated(t.t, t.tapd, rpcAssets[0], addr3) _, err = t.tapd.SendAsset(ctxt, &taprpc.SendAssetRequest{ @@ -163,7 +173,7 @@ func testFeeEstimation(t *harnessTest) { // After failure at the high feerate, we should still be able to make a // transfer at a very low feerate. t.lndHarness.SetFeeEstimateWithConf(lowFeeRate, 6) - sendResp = sendAssetsToAddr(t, t.tapd, addr3) + sendResp, sendEvents = sendAssetsToAddr(t, t.tapd, addr3) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, normalAssetId, @@ -172,6 +182,12 @@ func testFeeEstimation(t *harnessTest) { ) transferIdx += 1 AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) + AssertSendEvents( + t.t, addr3.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + AssertReceiveEvents(t.t, addr3, stream3) sendInputAmt = anchorAmounts[3] + 1000 AssertTransferFeeRate( diff --git a/itest/full_value_split_test.go b/itest/full_value_split_test.go index 84afb2430..c50bcab6c 100644 --- a/itest/full_value_split_test.go +++ b/itest/full_value_split_test.go @@ -3,6 +3,7 @@ package itest import ( "context" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/stretchr/testify/require" @@ -77,7 +78,9 @@ func runFullValueSendTests(ctxt context.Context, t *harnessTest, alice, require.NoError(t.t, err) AssertAddrCreated(t.t, bob, mintedAsset, receiverAddr) - sendResp := sendAssetsToAddr(t, alice, receiverAddr) + sendResp, sendEvents := sendAssetsToAddr( + t, alice, receiverAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, alice, sendResp, genInfo.AssetId, @@ -87,6 +90,11 @@ func runFullValueSendTests(ctxt context.Context, t *harnessTest, alice, AssertNonInteractiveRecvComplete( t.t, bob, senderTransferIdx+1, ) + AssertSendEvents( + t.t, receiverAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) senderTransferIdx++ } else { receiverAddr, err = alice.NewAddr( @@ -98,7 +106,9 @@ func runFullValueSendTests(ctxt context.Context, t *harnessTest, alice, require.NoError(t.t, err) AssertAddrCreated(t.t, alice, mintedAsset, receiverAddr) - sendResp := sendAssetsToAddr(t, bob, receiverAddr) + sendResp, sendEvents := sendAssetsToAddr( + t, bob, receiverAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, bob, sendResp, genInfo.AssetId, []uint64{0, fullAmount}, @@ -107,6 +117,11 @@ func runFullValueSendTests(ctxt context.Context, t *harnessTest, alice, AssertNonInteractiveRecvComplete( t.t, alice, receiverTransferIdx+1, ) + AssertSendEvents( + t.t, receiverAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) receiverTransferIdx++ } } diff --git a/itest/interface.go b/itest/interface.go index bc5157739..876fc4ccf 100644 --- a/itest/interface.go +++ b/itest/interface.go @@ -5,6 +5,7 @@ import ( "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" + "github.com/lightninglabs/taproot-assets/taprpc/tapdevrpc" unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc" ) @@ -15,4 +16,5 @@ type TapdClient interface { mintrpc.MintClient rfqrpc.RfqClient assetwalletrpc.AssetWalletClient + tapdevrpc.TapDevClient } diff --git a/itest/multi_asset_group_test.go b/itest/multi_asset_group_test.go index c95892078..69f3d1efc 100644 --- a/itest/multi_asset_group_test.go +++ b/itest/multi_asset_group_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/stretchr/testify/require" @@ -119,13 +120,20 @@ func testMintMultiAssetGroups(t *harnessTest) { }) require.NoError(t.t, err) - normalGroupSend := sendAssetsToAddr(t, t.tapd, bobNormalAddr) + normalGroupSend, normalSendEvents := sendAssetsToAddr( + t, t.tapd, bobNormalAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, normalGroupSend, normalMember.AssetGenesis.AssetId, []uint64{0, normalMember.Amount}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, 1) + AssertSendEvents( + t.t, bobNormalAddr.ScriptKey, normalSendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) AssertBalanceByGroup( t.t, secondTapd, normalGroupKey, normalMember.Amount, @@ -156,13 +164,20 @@ func testMintMultiAssetGroups(t *harnessTest) { }) require.NoError(t.t, err) - collectGroupSend := sendAssetsToAddr(t, t.tapd, bobCollectAddr) + collectGroupSend, groupSendEvents := sendAssetsToAddr( + t, t.tapd, bobCollectAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, collectGroupSend, collectMember.AssetGenesis.AssetId, []uint64{0, collectMember.Amount}, 1, 2, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, 2) + AssertSendEvents( + t.t, bobCollectAddr.ScriptKey, groupSendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) AssertBalanceByGroup( t.t, secondTapd, collectGroupKey, collectMember.Amount, @@ -354,7 +369,7 @@ func testMultiAssetGroupSend(t *harnessTest) { require.NoError(t.t, err) AssertAddrCreated(t.t, secondTapd, sendAsset, addr) - sendResp := sendAssetsToAddr(t, t.tapd, addr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, @@ -363,6 +378,11 @@ func testMultiAssetGroupSend(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, secondTapd, i+1) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } } diff --git a/itest/psbt_test.go b/itest/psbt_test.go index 3b9634306..da6958ba4 100644 --- a/itest/psbt_test.go +++ b/itest/psbt_test.go @@ -15,6 +15,7 @@ import ( "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/internal/test" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/taprpc" wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" @@ -889,6 +890,21 @@ func testPsbtMultiSend(t *harnessTest) { ) require.NoError(t.t, err) + // Before we anchor the transaction, we'll subscribe for send events, so + // we can track the state of the parcel. + ctxc, streamCancel := context.WithCancel(ctxb) + scriptKey1Bytes := receiverScriptKey1.PubKey.SerializeCompressed() + stream, err := sender.SubscribeSendEvents( + ctxc, &taprpc.SubscribeSendEventsRequest{ + FilterScriptKey: scriptKey1Bytes, + }, + ) + require.NoError(t.t, err) + sendEvents := &EventSubscription[*taprpc.SendEvent]{ + ClientEventStream: stream, + Cancel: streamCancel, + } + // Now we'll attempt to complete the transfer. sendResp, err := sender.AnchorVirtualPsbts( ctxt, &wrpc.AnchorVirtualPsbtsRequest{ @@ -897,6 +913,12 @@ func testPsbtMultiSend(t *harnessTest) { ) require.NoError(t.t, err) + AssertSendEvents( + t.t, scriptKey1Bytes, sendEvents, + tapfreighter.SendStateAnchorSign, + tapfreighter.SendStateWaitTxConf, + ) + // We end up with a transfer with 5 outputs: 2 for the two different // receiver addresses (with an anchor output each), 2 for the sender // addresses (sharing an anchor output) and 1 for the change. So there @@ -907,11 +929,16 @@ func testPsbtMultiSend(t *harnessTest) { genInfo.AssetId, outputAmounts, 0, 1, numOutputs, ) + AssertSendEvents( + t.t, scriptKey1Bytes, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + // This is an interactive transfer, so we do need to manually send the // proof from the sender to the receiver. _ = sendProof( - t, sender, receiver, sendResp, - receiverScriptKey1.PubKey.SerializeCompressed(), genInfo, + t, sender, receiver, sendResp, scriptKey1Bytes, genInfo, ) _ = sendProof( t, sender, receiver, sendResp, @@ -1002,7 +1029,7 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { AssertAddrCreated(t.t, secondaryTapd, rpcAsset, addr) // Send the assets to the secondary node. - sendResp := sendAssetsToAddr(t, primaryTapd, addr) + sendResp, sendEvents := sendAssetsToAddr(t, primaryTapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp, @@ -1010,6 +1037,11 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 1) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) primaryTapdAssetAmt -= sendAmt @@ -1029,7 +1061,7 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { AssertAddrCreated(t.t, secondaryTapd, rpcAsset, addr) // Send the assets to the secondary node. - sendResp = sendAssetsToAddr(t, primaryTapd, addr) + sendResp, sendEvents = sendAssetsToAddr(t, primaryTapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp, @@ -1037,6 +1069,11 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 2) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) primaryTapdAssetAmt -= sendAmt @@ -1055,7 +1092,7 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { AssertAddrCreated(t.t, secondaryTapd, rpcAsset, addr) // Send the assets to the secondary node. - sendResp = sendAssetsToAddr(t, primaryTapd, addr) + sendResp, sendEvents = sendAssetsToAddr(t, primaryTapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, primaryTapd, sendResp, @@ -1063,6 +1100,11 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, secondaryTapd, 3) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) primaryTapdAssetAmt -= sendAmt @@ -1671,7 +1713,7 @@ func sendToTapscriptAddr(ctx context.Context, t *harnessTest, alice, // Send the asset to Bob using the script key with an actual script // tree. - sendResp := sendAssetsToAddr(t, alice, bobAddr) + sendResp, sendEvents := sendAssetsToAddr(t, alice, bobAddr) changeUnits := mintedAsset.Amount - numUnits ConfirmAndAssertOutboundTransfer( @@ -1679,6 +1721,11 @@ func sendToTapscriptAddr(ctx context.Context, t *harnessTest, alice, genInfo.AssetId, []uint64{changeUnits, numUnits}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, bob, 1) + AssertSendEvents( + t.t, bobAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } func sendAssetAndAssert(ctx context.Context, t *harnessTest, alice, @@ -1696,7 +1743,7 @@ func sendAssetAndAssert(ctx context.Context, t *harnessTest, alice, require.NoError(t.t, err) AssertAddrCreated(t.t, bob, mintedAsset, bobAddr) - sendResp := sendAssetsToAddr(t, alice, bobAddr) + sendResp, sendEvents := sendAssetsToAddr(t, alice, bobAddr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, alice, sendResp, genInfo.AssetId, []uint64{change, numUnits}, outTransferIdx, @@ -1706,4 +1753,9 @@ func sendAssetAndAssert(ctx context.Context, t *harnessTest, alice, // There are now two receive events (since only non-interactive sends // appear in that RPC output). AssertNonInteractiveRecvComplete(t.t, bob, numInTransfers) + AssertSendEvents( + t.t, bobAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } diff --git a/itest/re-issuance_test.go b/itest/re-issuance_test.go index a043fb1df..ae62f5553 100644 --- a/itest/re-issuance_test.go +++ b/itest/re-issuance_test.go @@ -6,6 +6,7 @@ import ( "math" "github.com/lightninglabs/taproot-assets/mssmt" + "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/stretchr/testify/require" @@ -68,12 +69,19 @@ func testReIssuance(t *harnessTest) { ) require.NoError(t.t, err) - firstCollectSend := sendAssetsToAddr(t, t.tapd, collectGroupAddr) + firstCollectSend, firstCollectEvents := sendAssetsToAddr( + t, t.tapd, collectGroupAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, firstCollectSend, collectGenInfo.AssetId, []uint64{0, 1}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, 1) + AssertSendEvents( + t.t, collectGroupAddr.ScriptKey, firstCollectEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Check the state of both nodes. The first node should show one // zero-value transfer representing the send of the collectible. @@ -93,13 +101,20 @@ func testReIssuance(t *harnessTest) { ) require.NoError(t.t, err) - firstNormalSend := sendAssetsToAddr(t, t.tapd, normalGroupAddr) + firstNormalSend, firstNormalEvents := sendAssetsToAddr( + t, t.tapd, normalGroupAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, firstNormalSend, normalGenInfo.AssetId, []uint64{normalGroupMintHalf, normalGroupMintHalf}, 1, 2, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, 2) + AssertSendEvents( + t.t, normalGroupAddr.ScriptKey, firstNormalEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Reissue one more collectible and half the original mint amount for // the normal asset. @@ -170,12 +185,19 @@ func testReIssuance(t *harnessTest) { ) require.NoError(t.t, err) - secondCollectSend := sendAssetsToAddr(t, t.tapd, collectReissueAddr) + secondCollectSend, secondCollectEvents := sendAssetsToAddr( + t, t.tapd, collectReissueAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, secondCollectSend, collectReissueInfo.AssetId, []uint64{0, 1}, 2, 3, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, 3) + AssertSendEvents( + t.t, collectReissueAddr.ScriptKey, secondCollectEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // The second node should show two groups, with two assets in // the collectible group and a total balance of 2 for that group. @@ -201,12 +223,19 @@ func testReIssuance(t *harnessTest) { ) require.NoError(t.t, err) - thirdCollectSend := sendAssetsToAddr(t, secondTapd, collectGenAddr) + thirdCollectSend, thirdCollectEvents := sendAssetsToAddr( + t, secondTapd, collectGenAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, secondTapd.ht.lndHarness.Miner.Client, secondTapd, thirdCollectSend, collectGenInfo.AssetId, []uint64{0, 1}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, t.tapd, 1) + AssertSendEvents( + t.t, collectGenAddr.ScriptKey, thirdCollectEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // The collectible balance on the minting node should be 1, and there // should still be only two groups. @@ -356,11 +385,18 @@ func testMintWithGroupKeyErrors(t *harnessTest) { ) require.NoError(t.t, err) - collectSend := sendAssetsToAddr(t, t.tapd, collectGroupAddr) + collectSend, collectEvents := sendAssetsToAddr( + t, t.tapd, collectGroupAddr, + ) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, collectSend, collectGenInfo.AssetId, []uint64{0, 1}, 0, 1, ) + AssertSendEvents( + t.t, collectGroupAddr.ScriptKey, collectEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // A re-issuance with the second node should still fail because the // group key was not created by that node. diff --git a/itest/re-org_test.go b/itest/re-org_test.go index 21e29a79e..4120c34dd 100644 --- a/itest/re-org_test.go +++ b/itest/re-org_test.go @@ -156,7 +156,7 @@ func testReOrgSend(t *harnessTest) { }) require.NoError(t.t, err) AssertAddrCreated(t.t, secondTapd, sendAsset, bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, _ := sendAssetsToAddr(t, t.tapd, bobAddr) initialBlock := ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, sendAssetGen.AssetId, @@ -270,7 +270,7 @@ func testReOrgMintAndSend(t *harnessTest) { }) require.NoError(t.t, err) AssertAddrCreated(t.t, secondTapd, sendAsset, bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, _ := sendAssetsToAddr(t, t.tapd, bobAddr) initialBlock := ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, sendAssetGen.AssetId, diff --git a/itest/round_trip_send_test.go b/itest/round_trip_send_test.go index 96bfd0183..57b929606 100644 --- a/itest/round_trip_send_test.go +++ b/itest/round_trip_send_test.go @@ -68,7 +68,7 @@ func testRoundTripSend(t *harnessTest) { require.NoError(t.t, err) AssertAddrCreated(t.t, secondTapd, rpcAssets[0], bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, _ := sendAssetsToAddr(t, t.tapd, bobAddr) sendRespJSON, err := formatProtoJSON(sendResp) require.NoError(t.t, err) t.Logf("Got response from sending assets: %v", sendRespJSON) @@ -88,7 +88,7 @@ func testRoundTripSend(t *harnessTest) { require.NoError(t.t, err) AssertAddrCreated(t.t, t.tapd, rpcAssets[0], aliceAddr) - sendResp = sendAssetsToAddr(t, secondTapd, aliceAddr) + sendResp, _ = sendAssetsToAddr(t, secondTapd, aliceAddr) sendRespJSON, err = formatProtoJSON(sendResp) require.NoError(t.t, err) t.Logf("Got response from sending assets: %v", sendRespJSON) diff --git a/itest/send_test.go b/itest/send_test.go index f9c29205e..0d835c2e1 100644 --- a/itest/send_test.go +++ b/itest/send_test.go @@ -105,7 +105,7 @@ func testBasicSendUnidirectional(t *harnessTest) { AssertAddrCreated(t.t, secondTapd, rpcAssets[0], bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, bobAddr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, @@ -113,6 +113,11 @@ func testBasicSendUnidirectional(t *harnessTest) { []uint64{currentUnits, numUnits}, i, i+1, ) AssertNonInteractiveRecvComplete(t.t, secondTapd, i+1) + AssertSendEvents( + t.t, bobAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } // Close event stream. @@ -216,7 +221,7 @@ func testRestartReceiverCheckBalance(t *harnessTest) { AssertAddrCreated(t.t, recvTapd, rpcAssets[0], bobAddr) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, bobAddr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, @@ -224,6 +229,11 @@ func testRestartReceiverCheckBalance(t *harnessTest) { []uint64{currentUnits, numUnits}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, recvTapd, 1) + AssertSendEvents( + t.t, bobAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Close event stream. err = events.CloseSend() @@ -449,7 +459,7 @@ func testBasicSendPassiveAsset(t *harnessTest) { AssertAddrCreated(t.t, recvTapd, firstAsset, recvAddr) // Send the assets to the receiving node. - sendResp := sendAssetsToAddr(t, t.tapd, recvAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, recvAddr) addProofTestVectorFromProof( t.t, "valid regtest proof for split root", testVectors, @@ -469,6 +479,11 @@ func testBasicSendPassiveAsset(t *harnessTest) { []uint64{expectedAmtAfterSend, numUnitsSend}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, recvTapd, 1) + AssertSendEvents( + t.t, recvAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Assert that the sending node returns the correct asset list via RPC. AssertListAssets( @@ -494,7 +509,7 @@ func testBasicSendPassiveAsset(t *harnessTest) { AssertAddrCreated(t.t, recvTapd, secondAsset, recvAddr) // Send the assets to the receiving node. - sendResp = sendAssetsToAddr(t, t.tapd, recvAddr) + sendResp, sendEvents = sendAssetsToAddr(t, t.tapd, recvAddr) // Assert that the outbound transfer was confirmed. expectedAmtAfterSend = assets[1].Asset.Amount - numUnitsSend @@ -505,6 +520,11 @@ func testBasicSendPassiveAsset(t *harnessTest) { []uint64{expectedAmtAfterSend, numUnitsSend}, 1, 2, ) AssertNonInteractiveRecvComplete(t.t, recvTapd, 2) + AssertSendEvents( + t.t, recvAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // And now send part of the first asset back again, so we get a bit of a // longer proof chain in the file. @@ -516,7 +536,7 @@ func testBasicSendPassiveAsset(t *harnessTest) { AssertAddrCreated(t.t, t.tapd, firstAsset, newAddr) // Send the assets back to the first node. - sendResp = sendAssetsToAddr(t, recvTapd, newAddr) + sendResp, sendEvents = sendAssetsToAddr(t, recvTapd, newAddr) // Assert that the outbound transfer was confirmed. expectedAmtAfterSend = numUnitsSend - numUnitsSend/2 @@ -526,6 +546,11 @@ func testBasicSendPassiveAsset(t *harnessTest) { []uint64{expectedAmtAfterSend, numUnitsSend / 2}, 0, 1, ) AssertNonInteractiveRecvComplete(t.t, t.tapd, 1) + AssertSendEvents( + t.t, newAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // We also want to generate an ownership proof of the asset we received // back. @@ -1005,7 +1030,7 @@ func testOfflineReceiverEventuallyReceives(t *harnessTest) { // was received. This function will block until the event is received or the // event stream is closed. func assertAssetSendNtfsEvent(t *harnessTest, - stream *eventSubscription[*tapdevrpc.SendAssetEvent], + stream *EventSubscription[*tapdevrpc.SendAssetEvent], timeout time.Duration, targetEventSelector func(*tapdevrpc.SendAssetEvent) bool, expectedCount int) { @@ -1018,7 +1043,7 @@ func assertAssetSendNtfsEvent(t *harnessTest, go func() { select { case <-timeoutChan: - stream.cancel() + stream.Cancel() case <-success: } @@ -1059,7 +1084,7 @@ func assertAssetSendNtfsEvent(t *harnessTest, // notification was received. This function will block until the event is // received or the event stream is closed. func assertAssetRecvNtfsEvent(t *harnessTest, timeout time.Duration, - stream *eventSubscription[*tapdevrpc.ReceiveAssetEvent], + stream *EventSubscription[*tapdevrpc.ReceiveAssetEvent], targetEventSelector func(event *tapdevrpc.ReceiveAssetEvent) bool, expectedCount int) { @@ -1071,7 +1096,7 @@ func assertAssetRecvNtfsEvent(t *harnessTest, timeout time.Duration, go func() { select { case <-timeoutChan: - stream.cancel() + stream.Cancel() case <-success: } @@ -1117,7 +1142,7 @@ func assertAssetRecvNtfsEvent(t *harnessTest, timeout time.Duration, // received or the event stream is closed. func assertAssetRecvCompleteEvent(t *harnessTest, timeout time.Duration, encodedAddr string, - stream *eventSubscription[*tapdevrpc.ReceiveAssetEvent]) { + stream *EventSubscription[*tapdevrpc.ReceiveAssetEvent]) { eventSelector := func(event *tapdevrpc.ReceiveAssetEvent) bool { switch eventTyped := event.Event.(type) { @@ -1174,7 +1199,7 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { AssertAddrCreated(t.t, bobTapd, rpcAsset, addr) // Send the assets to the secondary node. - sendResp := sendAssetsToAddr(t, t.tapd, addr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, @@ -1182,6 +1207,11 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, bobTapd, 1) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Second of two send events from minting node to the secondary node. addr, err = bobTapd.NewAddr( @@ -1194,7 +1224,7 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { AssertAddrCreated(t.t, bobTapd, rpcAsset, addr) // Send the assets to the secondary node. - sendResp = sendAssetsToAddr(t, t.tapd, addr) + sendResp, sendEvents = sendAssetsToAddr(t, t.tapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, @@ -1202,6 +1232,11 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, bobTapd, 2) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) t.Logf("Two separate send events complete, now attempting to send " + "back the full amount in a single multi input send event") @@ -1217,7 +1252,7 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { AssertAddrCreated(t.t, t.tapd, rpcAsset, addr) // Send the assets to the minting node. - sendResp = sendAssetsToAddr(t, bobTapd, addr) + sendResp, sendEvents = sendAssetsToAddr(t, bobTapd, addr) ConfirmAndAssertOutboundTransfer( t.t, t.lndHarness.Miner.Client, bobTapd, sendResp, @@ -1225,6 +1260,11 @@ func testMultiInputSendNonInteractiveSingleID(t *harnessTest) { ) AssertNonInteractiveRecvComplete(t.t, t.tapd, 1) + AssertSendEvents( + t.t, addr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } // testSendMultipleCoins tests that we can send multiple transfers at the same @@ -1270,7 +1310,7 @@ func testSendMultipleCoins(t *harnessTest) { // We created 5 addresses in our first node now, so we can initiate the // transfer to send the coins back to our wallet in 5 pieces now. - sendResp := sendAssetsToAddr(t, t.tapd, addrs...) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, addrs...) ConfirmAndAssertOutboundTransferWithOutputs( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, genInfo.AssetId, []uint64{ @@ -1279,10 +1319,18 @@ func testSendMultipleCoins(t *harnessTest) { }, 0, 1, numParts+1, ) AssertNonInteractiveRecvComplete(t.t, t.tapd, 5) + AssertSendEvents( + t.t, addrs[0].ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) // Next, we'll attempt to complete 5 parallel transfers with distinct // addresses from our main node to Bob. bobAddrs := make([]*taprpc.Addr, numParts) + addrSendEvents := make( + []*EventSubscription[*taprpc.SendEvent], numParts, + ) for i := 0; i < numParts; i++ { var err error bobAddrs[i], err = secondTapd.NewAddr( @@ -1293,7 +1341,9 @@ func testSendMultipleCoins(t *harnessTest) { ) require.NoError(t.t, err) - sendResp := sendAssetsToAddr(t, t.tapd, bobAddrs[i]) + sendResp, addrSendEvents[i] = sendAssetsToAddr( + t, t.tapd, bobAddrs[i], + ) AssertAssetOutboundTransferWithOutputs( t.t, t.lndHarness.Miner.Client, t.tapd, sendResp.Transfer, genInfo.AssetId, @@ -1323,6 +1373,13 @@ func testSendMultipleCoins(t *harnessTest) { // expected. _ = MineBlocks(t.t, t.lndHarness.Miner.Client, 1, 5) AssertNonInteractiveRecvComplete(t.t, secondTapd, 5) + for idx, events := range addrSendEvents { + AssertSendEvents( + t.t, bobAddrs[idx].ScriptKey, events, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) + } } // testSendNoCourierUniverseImport tests that we can send assets to a node that @@ -1367,7 +1424,7 @@ func testSendNoCourierUniverseImport(t *harnessTest) { AssertAddrCreated(t.t, secondTapd, firstAsset, receiveAddr) // Send the assets to the receiving node. - sendResp := sendAssetsToAddr(t, t.tapd, receiveAddr) + sendResp, sendEvents := sendAssetsToAddr(t, t.tapd, receiveAddr) // Assert that the outbound transfer was confirmed. expectedAmtAfterSend := firstAsset.Amount - numUnitsSend @@ -1384,6 +1441,11 @@ func testSendNoCourierUniverseImport(t *harnessTest) { // And now, the transfer should be completed on the receiver side too. AssertNonInteractiveRecvComplete(t.t, secondTapd, 1) + AssertSendEvents( + t.t, receiveAddr.ScriptKey, sendEvents, + tapfreighter.SendStateStoreProofs, + tapfreighter.SendStateComplete, + ) } // addProofTestVectorFromFile adds a proof test vector by extracting it from the diff --git a/itest/utils.go b/itest/utils.go index e5d7377e5..54a2bc102 100644 --- a/itest/utils.go +++ b/itest/utils.go @@ -29,18 +29,18 @@ var ( regtestParams = &chaincfg.RegressionNetParams ) -// clientEventStream is a generic interface for a client stream that allows us +// ClientEventStream is a generic interface for a client stream that allows us // to receive events from a server. -type clientEventStream[T any] interface { +type ClientEventStream[T any] interface { Recv() (T, error) grpc.ClientStream } -// eventSubscription holds a generic client stream and its context cancel +// EventSubscription holds a generic client stream and its context cancel // function. -type eventSubscription[T any] struct { - clientEventStream[T] - cancel context.CancelFunc +type EventSubscription[T any] struct { + ClientEventStream[T] + Cancel context.CancelFunc } // CopyRequest is a helper function to copy a request so that we can modify it. @@ -409,12 +409,14 @@ func MintAssetsConfirmBatch(t *testing.T, minerClient *rpcclient.Client, batch := batchResp.Batches[0] require.NotEmpty(t, batch.BatchTxid) - return AssertAssetsMinted(t, tapClient, assetRequests, mintTXID, blockHash) + return AssertAssetsMinted( + t, tapClient, assetRequests, mintTXID, blockHash, + ) } // SubscribeSendEvents subscribes to send events and returns the event stream. func SubscribeSendEvents(t *testing.T, - tapd TapdClient) *eventSubscription[*tapdevrpc.SendAssetEvent] { + tapd TapdClient) *EventSubscription[*tapdevrpc.SendAssetEvent] { ctxb := context.Background() ctxt, cancel := context.WithCancel(ctxb) @@ -424,16 +426,16 @@ func SubscribeSendEvents(t *testing.T, ) require.NoError(t, err) - return &eventSubscription[*tapdevrpc.SendAssetEvent]{ - clientEventStream: stream, - cancel: cancel, + return &EventSubscription[*tapdevrpc.SendAssetEvent]{ + ClientEventStream: stream, + Cancel: cancel, } } // SubscribeReceiveEvents subscribes to receive events and returns the event // stream. func SubscribeReceiveEvents(t *testing.T, - tapd TapdClient) *eventSubscription[*tapdevrpc.ReceiveAssetEvent] { + tapd TapdClient) *EventSubscription[*tapdevrpc.ReceiveAssetEvent] { ctxb := context.Background() ctxt, cancel := context.WithCancel(ctxb) @@ -443,9 +445,9 @@ func SubscribeReceiveEvents(t *testing.T, ) require.NoError(t, err) - return &eventSubscription[*tapdevrpc.ReceiveAssetEvent]{ - clientEventStream: stream, - cancel: cancel, + return &EventSubscription[*tapdevrpc.ReceiveAssetEvent]{ + ClientEventStream: stream, + Cancel: cancel, } } @@ -453,7 +455,7 @@ func SubscribeReceiveEvents(t *testing.T, // event stream for receive events for the address. func NewAddrWithEventStream(t *testing.T, tapd TapdClient, req *taprpc.NewAddrRequest) (*taprpc.Addr, - *eventSubscription[*taprpc.ReceiveEvent]) { + *EventSubscription[*taprpc.ReceiveEvent]) { ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) @@ -463,7 +465,6 @@ func NewAddrWithEventStream(t *testing.T, tapd TapdClient, require.NoError(t, err) ctxc, cancel := context.WithCancel(ctxb) - stream, err := tapd.SubscribeReceiveEvents( ctxc, &taprpc.SubscribeReceiveEventsRequest{ FilterAddr: addr.Encoded, @@ -471,8 +472,8 @@ func NewAddrWithEventStream(t *testing.T, tapd TapdClient, ) require.NoError(t, err) - return addr, &eventSubscription[*taprpc.ReceiveEvent]{ - clientEventStream: stream, - cancel: cancel, + return addr, &EventSubscription[*taprpc.ReceiveEvent]{ + ClientEventStream: stream, + Cancel: cancel, } }