diff --git a/commitment/commitment_test.go b/commitment/commitment_test.go index 25fe3163b..44ea7ed7b 100644 --- a/commitment/commitment_test.go +++ b/commitment/commitment_test.go @@ -3,6 +3,7 @@ package commitment import ( "bytes" "context" + "crypto/sha256" "encoding/hex" "math/rand" "testing" @@ -1483,7 +1484,9 @@ func TestIsTaprootAssetCommitmentScript(t *testing.T) { t.Parallel() require.True(t, IsTaprootAssetCommitmentScript(testTapCommitmentScript)) - require.False(t, IsTaprootAssetCommitmentScript(TaprootAssetsMarker[:])) + + tag := sha256.Sum256(markerV2) + require.False(t, IsTaprootAssetCommitmentScript(tag[:])) } // TestAssetCommitmentNoWitness tests that an asset commitment of a v1 asset is diff --git a/commitment/tap_test.go b/commitment/tap_test.go new file mode 100644 index 000000000..272046a63 --- /dev/null +++ b/commitment/tap_test.go @@ -0,0 +1,79 @@ +package commitment + +import ( + "crypto/sha256" + "testing" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/internal/test" + "github.com/stretchr/testify/require" +) + +var ( + // markerV2 is the marker tag for Taproot Assets used in V2 commitments. + markerV2 = []byte(taprootAssetsMarkerTag + ":194243") +) + +// randTapCommitment generates a random Taproot commitment for the given +// assetVersion. +func randTapCommitment(t *testing.T, commitVersion *TapCommitmentVersion, + assetVersion asset.Version) *TapCommitment { + + t.Helper() + randAssetType := test.RandFlip(asset.Normal, asset.Collectible) + randGenesis := asset.RandGenesis(t, randAssetType) + randAsset := randAsset(t, randGenesis, nil) + + randAsset.Version = assetVersion + + tapCommitment, err := FromAssets(commitVersion, randAsset) + require.NoError(t, err) + + return tapCommitment +} + +// TestTaprootAssetsMarkerV0 tests if it can find the TaprootAssetsMarker V0 at +// the correct spot according to the legacy MarkerV0 digest with assetVersion 0 +// and 1. +func TestTaprootAssetsMarkerV0(t *testing.T) { + t.Parallel() + + assetVersions := []asset.Version{asset.V0, asset.V1} + for _, assetVersion := range assetVersions { + // Create a random Taproot commitment, and extract the tapLeaf + // script. + randTapCommitment := randTapCommitment(t, nil, assetVersion) + tapLeaf := randTapCommitment.TapLeaf() + script := tapLeaf.Script + + require.Equal(t, byte(assetVersion), script[0]) + require.Equal(t, TaprootAssetsMarker[:], script[1:33]) + } +} + +// TestTaprootAssetsMarkerV1 tests if it can find the TaprootAssetsMarker V2 at +// the correct spot according to the MarkerV1 digest with assetVersion 0 and 1. +func TestTaprootAssetsMarkerV1(t *testing.T) { + t.Parallel() + + assetVersions := []asset.Version{asset.V0, asset.V1} + for _, assetVersion := range assetVersions { + // Create a random Taproot commitment, and extract the tapLeaf + // script. + randTapCommitment := randTapCommitment( + t, fn.Ptr(TapCommitmentV2), assetVersion, + ) + + // Check if MarkerVersion is set to MarkerV2, which should be + // the default. + require.Equal(t, randTapCommitment.Version, TapCommitmentV2) + + tapLeaf := randTapCommitment.TapLeaf() + script := tapLeaf.Script + + tag := sha256.Sum256(markerV2) + require.Equal(t, tag[:], script[:32]) + require.Equal(t, byte(TapCommitmentV2), script[32]) + } +} diff --git a/commitment/taproot_test.go b/commitment/taproot_test.go index 67dcd79ae..60165945a 100644 --- a/commitment/taproot_test.go +++ b/commitment/taproot_test.go @@ -2,6 +2,7 @@ package commitment import ( "bytes" + "crypto/sha256" "encoding/binary" "encoding/hex" "testing" @@ -38,14 +39,26 @@ func TestTapscriptPreimage(t *testing.T) { // Create a random byte slice with the same structure as a Taproot // Asset commitment root, that can be used in a TapLeaf. - randTapCommitmentRoot := func(version asset.Version) []byte { + + randTapCommitmentRoot := func(version asset.Version, + legacyCommitment bool) []byte { + var dummyRootSum [8]byte binary.BigEndian.PutUint64( dummyRootSum[:], test.RandInt[uint64](), ) - dummyRootHashParts := [][]byte{ - {byte(version)}, TaprootAssetsMarker[:], - fn.ByteSlice(test.RandHash()), dummyRootSum[:], + var dummyRootHashParts [][]byte + if legacyCommitment { + dummyRootHashParts = [][]byte{ + {byte(version)}, TaprootAssetsMarker[:], + fn.ByteSlice(test.RandHash()), dummyRootSum[:], + } + } else { + tag := sha256.Sum256(markerV2) + dummyRootHashParts = [][]byte{ + tag[:], {byte(version)}, + fn.ByteSlice(test.RandHash()), dummyRootSum[:], + } } return bytes.Join(dummyRootHashParts, nil) } @@ -115,7 +128,33 @@ func TestTapscriptPreimage(t *testing.T) { }, { name: "tap commitment leaf pre-image", makePreimage: func(t *testing.T) *TapscriptPreimage { - tapCommitmentRoot := randTapCommitmentRoot(asset.V0) + tapCommitmentRoot := randTapCommitmentRoot( + asset.V0, false, + ) + var encodedLeaf bytes.Buffer + + _ = encodedLeaf.WriteByte( + byte(txscript.BaseLeafVersion), + ) + _ = wire.WriteVarBytes( + &encodedLeaf, 0, tapCommitmentRoot, + ) + + return &TapscriptPreimage{ + siblingType: LeafPreimage, + siblingPreimage: encodedLeaf.Bytes(), + } + }, + expectedType: LeafPreimage, + expectedName: "LeafPreimage", + expectedEmpty: false, + expectedHashErr: ErrPreimageIsTapCommitment.Error(), + }, { + name: "tap commitment leaf pre-image legacy commitment", + makePreimage: func(t *testing.T) *TapscriptPreimage { + tapCommitmentRoot := randTapCommitmentRoot( + asset.V0, true, + ) var encodedLeaf bytes.Buffer _ = encodedLeaf.WriteByte( diff --git a/itest/multisig.go b/itest/multisig.go index 28aa64c72..db1428cf1 100644 --- a/itest/multisig.go +++ b/itest/multisig.go @@ -501,6 +501,9 @@ type publishAndLogTransferOptions struct { // label is the label to use for the transfer. label string + + // expectedErr is the expected error when calling PublishAndLogTransfer. + expectedErr string } // defaultPublishAndLogTransferOptions returns the default options for @@ -525,6 +528,14 @@ func withLabel(label string) PublishAndLogTransferOption { } } +// withExpectedErr is an option for PublishAndLogTransfer that sets the +// expected error. +func withExpectedErr(expectedErr string) PublishAndLogTransferOption { + return func(opts *publishAndLogTransferOptions) { + opts.expectedErr = expectedErr + } +} + // PublishAndLogTransfer is a helper function that invokes the // PublishAndLogTransfer RPC endpoint on the specified tapd node. This endpoint // performs a pre-anchored transfer. @@ -572,7 +583,15 @@ func PublishAndLogTransfer(t *testing.T, tapd commands.RpcClientsBundle, } resp, err := tapd.PublishAndLogTransfer(ctxt, request) - require.NoError(t, err) + + if options.expectedErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), options.expectedErr, + "unexpected error: %v", err) + return nil + } else { + require.NoError(t, err) + } return resp } diff --git a/itest/psbt_test.go b/itest/psbt_test.go index 5c4935fd6..5b2d088a3 100644 --- a/itest/psbt_test.go +++ b/itest/psbt_test.go @@ -20,6 +20,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/cmd/commands" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/internal/test" @@ -343,6 +344,50 @@ func testPsbtNormalInteractiveFullValueSend(t *harnessTest) { ) } +// testPsbtMarkerV0MixedVersions tests that we get an error if we mix V0 and +// V2 commitments. +func testPsbtMarkerV0MixedVersions(t *harnessTest) { + // First, we'll make a normal asset with a bunch of units that we are + // going to send back and forth. We're also minting a passive asset that + // should remain where it is. + rpcAssets := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner().Client, t.tapd, + []*mintrpc.MintAssetRequest{ + simpleAssets[0], + // Our "passive" asset. + { + Asset: &mintrpc.MintAsset{ + AssetType: taprpc.AssetType_NORMAL, + Name: "itestbuxx-passive", + AssetMeta: &taprpc.AssetMeta{ + Data: []byte("some metadata"), + }, + Amount: 123, + }, + }, + }, + ) + + mintedAsset := rpcAssets[0] + genInfo := mintedAsset.AssetGenesis + + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + // Now that we have the asset created, we'll make a new node that'll + // serve as the node which'll receive the assets. + bobLnd := t.lndHarness.NewNodeWithCoins("Bob", nil) + secondTapd := setupTapdHarness(t.t, t, bobLnd, t.universeServer) + defer func() { + require.NoError(t.t, secondTapd.stop(!*noDelete)) + }() + + runPsbtInteractiveMarkerV0MixedVersions( + ctxt, t, t.tapd, secondTapd, genInfo, mintedAsset, + ) +} + // testPsbtMultiVersionSend tests that funding a V1 vPkt succeeds, but funding // a V0 vPkt fails. func testPsbtMultiVersionSend(t *harnessTest) { @@ -736,6 +781,84 @@ func runPsbtInteractiveFullValueSendTest(ctxt context.Context, t *harnessTest, ) } +// runPsbtInteractiveFullValueSendTest runs a single test of creating a mixed +// version asset commitment that should result in an error. +func runPsbtInteractiveMarkerV0MixedVersions(ctxt context.Context, + t *harnessTest, alice, bob *tapdHarness, genInfo *taprpc.GenesisInfo, + mintedAsset *taprpc.Asset) { + + var ( + sender = alice + receiver = bob + id [32]byte + fullAmt = mintedAsset.Amount + chainParams = &address.RegressionNetTap + ) + copy(id[:], genInfo.AssetId) + + // We need to derive two keys, one for the new script key and + // one for the internal key. + receiverScriptKey, receiverAnchorIntKeyDesc := DeriveKeys( + t.t, receiver, + ) + + vPkt := tappsbt.ForInteractiveSend( + id, fullAmt, receiverScriptKey, 0, 0, 0, + receiverAnchorIntKeyDesc, asset.V0, chainParams, + ) + + // Next, we'll attempt to complete a transfer with PSBTs from + // our sender node to our receiver, using the full amount. + fundResp := fundPacket(t, sender, vPkt) + + signResp, err := sender.SignVirtualPsbt( + ctxt, &wrpc.SignVirtualPsbtRequest{ + FundedPsbt: fundResp.FundedPsbt, + }, + ) + require.NoError(t.t, err) + + // Here we need to create a separate case for the 0th iteration. + // Instead of using AnchorVirtualPsbts we will use + // CommitVirtualPsbts and PublishAndLogTransfer. This way we can create + // a legacy marker commitment that we try to spend on the next + // iteration. + signedPkt := deserializeVPacket(t.t, signResp.SignedPsbt) + activePkt := []*tappsbt.VPacket{signedPkt} + + var passivePkt []*tappsbt.VPacket + for idx := range fundResp.PassiveAssetPsbts { + passiveBytes := fundResp.PassiveAssetPsbts[idx] + passiveSign, err := sender.SignVirtualPsbt( + ctxt, &wrpc.SignVirtualPsbtRequest{ + FundedPsbt: passiveBytes, + }, + ) + require.NoError(t.t, err) + + passivePkt = append(passivePkt, deserializeVPacket( + t.t, passiveSign.SignedPsbt, + )) + } + + allPackets := append(activePkt, passivePkt...) + btcPkt, err := tapsend.PrepareAnchoringTemplate(allPackets) + require.NoError(t.t, err) + var commitResp *wrpc.CommitVirtualPsbtsResponse + btcPkt, activePkt, passivePkt, commitResp = commitMarkerV0VirtualPsbts( + ctxt, t, sender, btcPkt, activePkt, passivePkt, -1, + ) + require.NoError(t.t, err) + + btcPkt = signPacket(t.t, sender.cfg.LndNode, btcPkt) + btcPkt = FinalizePacket(t.t, sender.cfg.LndNode.RPC, btcPkt) + PublishAndLogTransfer( + t.t, sender, btcPkt, activePkt, passivePkt, commitResp, + withExpectedErr("error validating input assets: mixed virtual "+ + "packet versions"), + ) +} + // testPsbtNormalInteractiveSplitSend tests that we can properly send normal // assets back and forth, using the full amount, between nodes with the use of // PSBTs. @@ -3745,3 +3868,196 @@ func getAddressBip32Derivation(t testing.TB, addr string, Bip32Path: path, } } + +// commitMarkerV0VirtualPsbts creates the output commitments and proofs for the +// given virtual transactions by committing them to the BTC level anchor +// transaction with MarkerV0 legacy commitment. +func commitMarkerV0VirtualPsbts(ctxt context.Context, t *harnessTest, + funder commands.RpcClientsBundle, packet *psbt.Packet, + activePackets []*tappsbt.VPacket, passivePackets []*tappsbt.VPacket, + changeOutputIndex int32) (*psbt.Packet, []*tappsbt.VPacket, + []*tappsbt.VPacket, *wrpc.CommitVirtualPsbtsResponse) { + + t.Logf("Funding packet: %v", spew.Sdump(packet)) + + var buf bytes.Buffer + err := packet.Serialize(&buf) + require.NoError(t.t, err) + + req := &wrpc.CommitVirtualPsbtsRequest{ + AnchorPsbt: buf.Bytes(), + Fees: &wrpc.CommitVirtualPsbtsRequest_SatPerVbyte{ + SatPerVbyte: uint64(feeRateSatPerKVByte / 1000), + }, + } + + type existingIndex = wrpc.CommitVirtualPsbtsRequest_ExistingOutputIndex + if changeOutputIndex < 0 { + req.AnchorChangeOutput = &wrpc.CommitVirtualPsbtsRequest_Add{ + Add: true, + } + } else { + req.AnchorChangeOutput = &existingIndex{ + ExistingOutputIndex: changeOutputIndex, + } + } + + req.VirtualPsbts = make([][]byte, len(activePackets)) + for idx := range activePackets { + req.VirtualPsbts[idx], err = tappsbt.Encode( + activePackets[idx], + ) + require.NoError(t.t, err) + } + req.PassiveAssetPsbts = make([][]byte, len(passivePackets)) + for idx := range passivePackets { + req.PassiveAssetPsbts[idx], err = tappsbt.Encode( + passivePackets[idx], + ) + require.NoError(t.t, err) + } + + // Now we can map the virtual packets to the PSBT. + commitResponse, err := funder.CommitVirtualPsbts(ctxt, req) + require.NoError(t.t, err) + + // Rework the output commitments. + activePackets = make( + []*tappsbt.VPacket, len(commitResponse.VirtualPsbts), + ) + for idx := range commitResponse.VirtualPsbts { + activePackets[idx], err = tappsbt.Decode( + commitResponse.VirtualPsbts[idx], + ) + require.NoError(t.t, err) + } + + passivePackets = make( + []*tappsbt.VPacket, len(commitResponse.PassiveAssetPsbts), + ) + for idx := range commitResponse.PassiveAssetPsbts { + passivePackets[idx], err = tappsbt.Decode( + commitResponse.PassiveAssetPsbts[idx], + ) + require.NoError(t.t, err) + } + + fundedPacket, err := psbt.NewFromRawBytes( + bytes.NewReader(commitResponse.AnchorPsbt), false, + ) + require.NoError(t.t, err) + + allPackets := append([]*tappsbt.VPacket{}, activePackets...) + allPackets = append(allPackets, passivePackets...) + + // We're going to re-create the output commitments using the v0 marker, + // which will add the alt leaves again. So we need to clear them first. + for _, vPkt := range allPackets { + for _, vOut := range vPkt.Outputs { + vOut.AltLeaves = nil + } + } + + // Recreate the output commitments. + outputCommitments, err := tapsend.CreateOutputCommitments(allPackets) + require.NoError(t.t, err) + + // Now we change the output commitments to `MarkerV0`. + for _, vPkt := range activePackets { + vPkt.Version = 0 + + err = updateTaprootOutputKeysMarkerV0( + fundedPacket, vPkt, outputCommitments, + ) + require.NoError(t.t, err) + } + + // New output commitments need new proof suffixes. + for idx := range activePackets { + vPkt := activePackets[idx] + + for vOutIdx := range vPkt.Outputs { + proofSuffix, err := tapsend.CreateProofSuffix( + fundedPacket.UnsignedTx, fundedPacket.Outputs, + vPkt, outputCommitments, + vOutIdx, allPackets, + ) + require.NoError(t.t, err) + + vPkt.Outputs[vOutIdx].ProofSuffix = proofSuffix + } + } + + commitResponse.AnchorPsbt, err = fn.Serialize(fundedPacket) + require.NoError(t.t, err) + + return fundedPacket, activePackets, passivePackets, commitResponse +} + +func updateTaprootOutputKeysMarkerV0(btcPacket *psbt.Packet, + vPkt *tappsbt.VPacket, + outputCommitments tappsbt.OutputCommitments) error { + + // Add the commitment outputs to the BTC level PSBT now. + for idx := range vPkt.Outputs { + vOut := vPkt.Outputs[idx] + anchorCommitment := outputCommitments[vOut.AnchorOutputIndex] + + // The commitment must be defined at this point. + if anchorCommitment == nil { + return tapsend.ErrMissingTapCommitment + } + + // The external output index cannot be out of bounds of the + // actual TX outputs. This should be checked earlier and is just + // a final safeguard here. + if vOut.AnchorOutputIndex >= uint32(len(btcPacket.Outputs)) { + return tapsend.ErrInvalidOutputIndexes + } + + btcOut := &btcPacket.Outputs[vOut.AnchorOutputIndex] + internalKey, err := schnorr.ParsePubKey( + btcOut.TaprootInternalKey, + ) + if err != nil { + return err + } + + // We have all the information now to calculate the actual + // commitment output script. + + anchorCommitment, err = anchorCommitment.Downgrade() + if err != nil { + return err + } + + //nolint:lll + script, merkleRoot, taprootAssetRoot, err := tapsend.AnchorOutputScript( + internalKey, vOut.AnchorOutputTapscriptSibling, + anchorCommitment, + ) + if err != nil { + return err + } + + btcTxOut := btcPacket.UnsignedTx.TxOut[vOut.AnchorOutputIndex] + btcTxOut.PkScript = script + + // Also set some additional fields in the PSBT output to make + // it easier to create the transfer database entry later. + btcOut.Unknowns = tappsbt.AddCustomField( + btcOut.Unknowns, + tappsbt.PsbtKeyTypeOutputTaprootMerkleRoot, + merkleRoot[:], + ) + btcOut.Unknowns = tappsbt.AddCustomField( + btcOut.Unknowns, + tappsbt.PsbtKeyTypeOutputAssetRoot, + taprootAssetRoot[:], + ) + + outputCommitments[vOut.AnchorOutputIndex] = anchorCommitment + } + + return nil +} diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index 8a2a72249..cefe39fb1 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -217,6 +217,10 @@ var allTestCases = []*testCase{ name: "psbt multi version send", test: testPsbtMultiVersionSend, }, + { + name: "psbt markerv0 mixed versions", + test: testPsbtMarkerV0MixedVersions, + }, { name: "psbt grouped interactive full value send", test: testPsbtGroupedInteractiveFullValueSend, diff --git a/tapgarden/planter_test.go b/tapgarden/planter_test.go index 53d059ab0..68487fe2f 100644 --- a/tapgarden/planter_test.go +++ b/tapgarden/planter_test.go @@ -3,6 +3,7 @@ package tapgarden_test import ( "bytes" "context" + "crypto/sha256" "database/sql" "encoding/binary" "encoding/hex" @@ -1579,9 +1580,11 @@ func testFinalizeWithTapscriptTree(t *mintingTestHarness) { // to one TapCommitment. var dummyRootSum [8]byte binary.BigEndian.PutUint64(dummyRootSum[:], test.RandInt[uint64]()) + + tag := sha256.Sum256([]byte("taproot-assets:194243")) dummyRootHashParts := [][]byte{ - {byte(asset.V0)}, commitment.TaprootAssetsMarker[:], - fn.ByteSlice(test.RandHash()), dummyRootSum[:], + tag[:], {byte(asset.V0)}, fn.ByteSlice(test.RandHash()), + dummyRootSum[:], } dummyTapCommitmentRootHash := bytes.Join(dummyRootHashParts, nil) dummyTapLeaf := txscript.NewBaseTapLeaf(dummyTapCommitmentRootHash) diff --git a/tapsend/proof_test.go b/tapsend/proof_test.go index dd402f1da..1ca8ec8d9 100644 --- a/tapsend/proof_test.go +++ b/tapsend/proof_test.go @@ -113,6 +113,10 @@ func createProofSuffix(t *testing.T, stxoProof bool, expectedErr string) { ) addBip86Output(t, anchorTx.FundedPsbt.Pkt) + // Change on outputCommitment to the legacy marker. + outputCommitments[2], err = outputCommitments[2].Downgrade() + require.NoError(t, err) + // Create a proof suffix for all 4 packets now and validate it. for _, vPkt := range testPackets { for outIdx := range vPkt.Outputs { diff --git a/tapsend/send_test.go b/tapsend/send_test.go index 9616f798b..9ca3093cc 100644 --- a/tapsend/send_test.go +++ b/tapsend/send_test.go @@ -1637,6 +1637,96 @@ var updateTaprootOutputKeysTestCases = []testCase{{ return nil }, err: nil, +}, { + name: "MarkerV0 asset send", + f: func(t *testing.T) error { + state := initSpendScenario(t) + + pkt := createPacket( + t, state.address1, state.asset1PrevID, + state, state.asset1InputAssets, true, + ) + err := tapsend.PrepareOutputAssets(context.Background(), pkt) + require.NoError(t, err) + err = tapsend.SignVirtualTransaction( + pkt, state.signer, state.witnessValidator, + ) + require.NoError(t, err) + + outputCommitments, err := tapsend.CreateOutputCommitments( + []*tappsbt.VPacket{pkt}, + ) + require.NoError(t, err) + + // Transform the output commitment into a MarkerV0 commitment. + // When UpdateTaprootOutputKeys is called is called, it will + // call ultimately commitment.TapLeaf which will then create a + // TapLeaf for the TapCommitment with the MarkerV0 digest. + recvIdx := pkt.Outputs[0].AnchorOutputIndex + + btcPkt, err := tapsend.CreateAnchorTx([]*tappsbt.VPacket{pkt}) + require.NoError(t, err) + + // Change on outputCommitment to the legacy marker. + for key := range outputCommitments { + c, err := outputCommitments[key].Downgrade() + require.NoError(t, err) + + outputCommitments[key] = c + } + + err = tapsend.UpdateTaprootOutputKeys( + btcPkt, pkt, outputCommitments, + ) + require.NoError(t, err) + + checkTaprootOutputs( + t, pkt.Outputs, outputCommitments, btcPkt, + &state.asset1, false, + ) + + receiverAsset := pkt.Outputs[0].Asset + receiverInternalKey, _ := schnorr.ParsePubKey( + btcPkt.Outputs[recvIdx].TaprootInternalKey, + ) + receiverTapTree := outputCommitments[recvIdx] + _, receiverTapProof, err := receiverTapTree.Proof( + receiverAsset.TapCommitmentKey(), + receiverAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + + receiverProof := &proof.TaprootProof{ + OutputIndex: recvIdx, + InternalKey: receiverInternalKey, + CommitmentProof: &proof.CommitmentProof{ + Proof: *receiverTapProof, + TapSiblingPreimage: nil, + }, + TapscriptProof: nil, + } + recvProofKeys, err := receiverProof.DeriveByAssetInclusion( + receiverAsset, nil, + ) + require.NoError(t, err) + + unsignedTxOut := btcPkt.UnsignedTx.TxOut + recvPsbtKey := unsignedTxOut[recvIdx].PkScript[2:] + + for proofKey, commit := range recvProofKeys { + if commit.Version == commitment.TapCommitmentV2 { + continue + } + + // Check that the output is a MarkerV0 commitment. + require.Equal( + t, proofKey.SchnorrSerialized(), recvPsbtKey, + ) + } + + return nil + }, + err: nil, }} func createSpend(t *testing.T, state *spendData, inputSet commitment.InputSet,