@@ -7,12 +7,15 @@ import (
77 "fmt"
88 "net/url"
99 "testing"
10+ "time"
1011
1112 "github.com/btcsuite/btcd/btcec/v2/schnorr"
1213 "github.com/btcsuite/btcd/btcutil"
1314 "github.com/btcsuite/btcd/btcutil/hdkeychain"
1415 "github.com/btcsuite/btcd/btcutil/psbt"
16+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1517 "github.com/btcsuite/btcd/txscript"
18+ "github.com/btcsuite/btcd/wire"
1619 "github.com/btcsuite/btcwallet/waddrmgr"
1720 "github.com/davecgh/go-spew/spew"
1821 "github.com/lightninglabs/taproot-assets/address"
@@ -2611,18 +2614,80 @@ func testPsbtExternalCommit(t *harnessTest) {
26112614
26122615 btcPacket = signPacket (t .t , aliceLnd , btcPacket )
26132616 btcPacket = FinalizePacket (t .t , aliceLnd .RPC , btcPacket )
2617+
2618+ transferLabel := "itest-psbt-external-commit"
2619+
2620+ // Subscribe to the send event stream so we can verify the sender's
2621+ // state transitions during this test.
2622+ ctx , streamCancel := context .WithCancel (ctxb )
2623+ stream , err := aliceTapd .SubscribeSendEvents (
2624+ ctx , & taprpc.SubscribeSendEventsRequest {
2625+ FilterLabel : transferLabel ,
2626+ },
2627+ )
2628+ require .NoError (t .t , err )
2629+ sendEvents := & EventSubscription [* taprpc.SendEvent ]{
2630+ ClientEventStream : stream ,
2631+ Cancel : streamCancel ,
2632+ }
2633+
2634+ // Execute the transfer but skip anchor transaction broadcast. This
2635+ // simulates a packaging workflow where broadcasting is handled
2636+ // externally.
26142637 sendResp := PublishAndLogTransfer (
26152638 t .t , aliceTapd , btcPacket , activeAssets , passiveAssets ,
2616- commitResp ,
2639+ commitResp , withSkipAnchorTxBroadcast (),
2640+ withLabel (transferLabel ),
26172641 )
26182642
2643+ // Assert that the state machine transitions directly to waiting for
2644+ // tx confirmation, skipping the broadcast state.
2645+ require .Eventually (t .t , func () bool {
2646+ isMatchingState := func (msg * taprpc.SendEvent ) bool {
2647+ lastState := tapfreighter .SendStateStorePreBroadcast
2648+ nextState := tapfreighter .SendStateWaitTxConf
2649+
2650+ return msg .SendState == lastState .String () &&
2651+ msg .NextSendState == nextState .String ()
2652+ }
2653+
2654+ for {
2655+ msg , err := sendEvents .Recv ()
2656+ if err != nil {
2657+ return false
2658+ }
2659+
2660+ return isMatchingState (msg )
2661+ }
2662+ }, defaultWaitTimeout , time .Second )
2663+
2664+ // Unmarshal the anchor transaction.
2665+ var anchorTx wire.MsgTx
2666+ reader := bytes .NewReader (sendResp .Transfer .AnchorTx )
2667+ err = anchorTx .Deserialize (reader )
2668+ require .NoError (t .t , err )
2669+
2670+ // Ensure that the anchor PSBT matches the returned anchor
2671+ // transaction.
2672+ require .Equal (t .t , anchorTx .TxHash (), btcPacket .UnsignedTx .TxHash ())
2673+
2674+ // Assert that the anchor transaction is not in the mempool.
2675+ miner := t .lndHarness .Miner ()
2676+ miner .AssertTxnsNotInMempool ([]chainhash.Hash {
2677+ anchorTx .TxHash (),
2678+ })
2679+
2680+ // Add the anchor transaction to the mempool and mine.
2681+ miner .MineBlockWithTx (& anchorTx )
2682+
2683+ // Assert that the transfer has the correct number of outputs.
26192684 expectedAmounts := []uint64 {
26202685 targetAsset .Amount - assetsToSend , assetsToSend ,
26212686 }
2622- ConfirmAndAssertOutboundTransferWithOutputs (
2687+ AssertAssetOutboundTransferWithOutputs (
26232688 t .t , t .lndHarness .Miner ().Client , aliceTapd ,
2624- sendResp , targetAssetGenesis .AssetId , expectedAmounts ,
2625- 0 , 1 , len (expectedAmounts ),
2689+ sendResp . Transfer , targetAssetGenesis .AssetId , expectedAmounts ,
2690+ 0 , 1 , len (expectedAmounts ), false ,
26262691 )
26272692
26282693 // And now the event should be completed on both sides.
0 commit comments