@@ -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"
@@ -1124,7 +1127,7 @@ func testPsbtInteractiveAltLeafAnchoring(t *harnessTest) {
11241127
11251128 commitPacket = signPacket (t .t , senderLnd , commitPacket )
11261129 commitPacket = FinalizePacket (t .t , senderLnd .RPC , commitPacket )
1127- publishResp := LogAndPublish (
1130+ publishResp := PublishAndLogTransfer (
11281131 t .t , sender , commitPacket , []* tappsbt.VPacket {activePacket },
11291132 []* tappsbt.VPacket {passivePacket }, commitResp ,
11301133 )
@@ -2438,7 +2441,7 @@ func testPsbtTrustlessSwap(t *harnessTest) {
24382441 signedPkt := finalizePacket (t .t , lndBob , finalPsbt )
24392442 require .True (t .t , signedPkt .IsComplete ())
24402443
2441- logResp := logAndPublish (
2444+ logResp := PublishAndLogTransfer (
24422445 t .t , alice , signedPkt , []* tappsbt.VPacket {bobVPsbt }, nil , resp ,
24432446 )
24442447 t .Logf ("Logged transaction: %v" , toJSON (t .t , logResp ))
@@ -2611,18 +2614,80 @@ func testPsbtExternalCommit(t *harnessTest) {
26112614
26122615 btcPacket = signPacket (t .t , aliceLnd , btcPacket )
26132616 btcPacket = FinalizePacket (t .t , aliceLnd .RPC , btcPacket )
2614- sendResp := LogAndPublish (
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.
2637+ 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.
@@ -3273,7 +3338,7 @@ func testPsbtRelativeLockTimeSendProofFail(t *harnessTest) {
32733338 Cancel : streamCancel ,
32743339 }
32753340
3276- LogAndPublish (t .t , bob , btcPacket , vPackets , nil , commitResp )
3341+ PublishAndLogTransfer (t .t , bob , btcPacket , vPackets , nil , commitResp )
32773342
32783343 MineBlocks (t .t , t .lndHarness .Miner ().Client , 1 , 1 )
32793344
@@ -3424,45 +3489,6 @@ func finalizePacket(t *testing.T, lnd *node.HarnessNode,
34243489 return signedPacket
34253490}
34263491
3427- func logAndPublish (t * testing.T , tapd * tapdHarness , btcPkt * psbt.Packet ,
3428- activeAssets []* tappsbt.VPacket , passiveAssets []* tappsbt.VPacket ,
3429- commitResp * wrpc.CommitVirtualPsbtsResponse ) * taprpc.SendAssetResponse {
3430-
3431- ctxb := context .Background ()
3432- ctxt , cancel := context .WithTimeout (ctxb , defaultWaitTimeout )
3433- defer cancel ()
3434-
3435- var buf bytes.Buffer
3436- err := btcPkt .Serialize (& buf )
3437- require .NoError (t , err )
3438-
3439- request := & wrpc.PublishAndLogRequest {
3440- AnchorPsbt : buf .Bytes (),
3441- VirtualPsbts : make ([][]byte , len (activeAssets )),
3442- PassiveAssetPsbts : make ([][]byte , len (passiveAssets )),
3443- ChangeOutputIndex : commitResp .ChangeOutputIndex ,
3444- LndLockedUtxos : commitResp .LndLockedUtxos ,
3445- }
3446-
3447- for idx := range activeAssets {
3448- request .VirtualPsbts [idx ], err = tappsbt .Encode (
3449- activeAssets [idx ],
3450- )
3451- require .NoError (t , err )
3452- }
3453- for idx := range passiveAssets {
3454- request .PassiveAssetPsbts [idx ], err = tappsbt .Encode (
3455- passiveAssets [idx ],
3456- )
3457- require .NoError (t , err )
3458- }
3459-
3460- resp , err := tapd .PublishAndLogTransfer (ctxt , request )
3461- require .NoError (t , err )
3462-
3463- return resp
3464- }
3465-
34663492// getAddressBip32Derivation returns the PSBT BIP-0032 derivation info of an
34673493// address.
34683494func getAddressBip32Derivation (t testing.TB , addr string ,
0 commit comments