Skip to content

Commit 7869f87

Browse files
committed
tapfreighter+tapsend: check input uniqueness
In this commit, we check for input asset uniqueness for parcels in the PreBroadcast state, before being converted to a Transfer. This prevents invalid transfers from being published and logged via RPC.
1 parent 0be95a6 commit 7869f87

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

tapfreighter/parcel.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ func ConvertToTransfer(currentHeight uint32, activeTransfers []*tappsbt.VPacket,
529529
PassiveAssetsAnchor: passiveAssetAnchor,
530530
}
531531

532+
allPackets := append(activeTransfers, passiveAssets...)
533+
if err := tapsend.AssertInputsUnique(allPackets); err != nil {
534+
return nil, fmt.Errorf("unable to convert to transfer: %w", err)
535+
}
536+
532537
for pIdx := range activeTransfers {
533538
vPkt := activeTransfers[pIdx]
534539

tapsend/send.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/lightninglabs/taproot-assets/fn"
2828
"github.com/lightninglabs/taproot-assets/tappsbt"
2929
"github.com/lightninglabs/taproot-assets/tapscript"
30+
lfn "github.com/lightningnetwork/lnd/fn"
3031
"github.com/lightningnetwork/lnd/input"
3132
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
3233
"golang.org/x/exp/maps"
@@ -1030,9 +1031,10 @@ func addKeyTweaks(unknowns []*psbt.Unknown, desc *lndclient.SignDescriptor) {
10301031
func CreateOutputCommitments(
10311032
packets []*tappsbt.VPacket) (tappsbt.OutputCommitments, error) {
10321033

1033-
// We create an empty output commitment map, keyed by the anchor output
1034-
// index.
1035-
outputCommitments := make(tappsbt.OutputCommitments)
1034+
// Inputs must be unique.
1035+
if err := AssertInputsUnique(packets); err != nil {
1036+
return nil, err
1037+
}
10361038

10371039
// We require all outputs that reference the same anchor output to be
10381040
// identical, otherwise some assumptions in the code below don't hold.
@@ -1052,6 +1054,10 @@ func CreateOutputCommitments(
10521054
return nil, err
10531055
}
10541056

1057+
// We create an empty output commitment map, keyed by the anchor output
1058+
// index.
1059+
outputCommitments := make(tappsbt.OutputCommitments)
1060+
10551061
// And now we commit each packet to the respective anchor output
10561062
// commitments.
10571063
for _, vPkt := range packets {
@@ -1544,6 +1550,26 @@ func AssertInputAnchorsEqual(packets []*tappsbt.VPacket) error {
15441550
return nil
15451551
}
15461552

1553+
// AssertInputsUnique makes sure that every input across all virtual packets is
1554+
// referencing a unique input asset, which is identified by the input PrevID.
1555+
func AssertInputsUnique(packets []*tappsbt.VPacket) error {
1556+
// PrevIDs are comparable enough to serve as a map key without hashing.
1557+
inputs := make(lfn.Set[asset.PrevID])
1558+
1559+
for _, vPkt := range packets {
1560+
for _, vIn := range vPkt.Inputs {
1561+
if inputs.Contains(vIn.PrevID) {
1562+
return fmt.Errorf("input %v is duplicated",
1563+
vIn.PrevID)
1564+
}
1565+
1566+
inputs.Add(vIn.PrevID)
1567+
}
1568+
}
1569+
1570+
return nil
1571+
}
1572+
15471573
// ExtractUnSpendable extracts all tombstones and burns from the active input
15481574
// commitment.
15491575
func ExtractUnSpendable(c *commitment.TapCommitment) []*asset.Asset {

0 commit comments

Comments
 (0)