Skip to content

Commit

Permalink
tapfreighter+tapsend: check input uniqueness
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
guggero committed Dec 6, 2024
1 parent 0be95a6 commit 7869f87
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
5 changes: 5 additions & 0 deletions tapfreighter/parcel.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,11 @@ func ConvertToTransfer(currentHeight uint32, activeTransfers []*tappsbt.VPacket,
PassiveAssetsAnchor: passiveAssetAnchor,
}

allPackets := append(activeTransfers, passiveAssets...)
if err := tapsend.AssertInputsUnique(allPackets); err != nil {
return nil, fmt.Errorf("unable to convert to transfer: %w", err)
}

for pIdx := range activeTransfers {
vPkt := activeTransfers[pIdx]

Expand Down
32 changes: 29 additions & 3 deletions tapsend/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/tappsbt"
"github.com/lightninglabs/taproot-assets/tapscript"
lfn "github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"golang.org/x/exp/maps"
Expand Down Expand Up @@ -1030,9 +1031,10 @@ func addKeyTweaks(unknowns []*psbt.Unknown, desc *lndclient.SignDescriptor) {
func CreateOutputCommitments(
packets []*tappsbt.VPacket) (tappsbt.OutputCommitments, error) {

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

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

// We create an empty output commitment map, keyed by the anchor output
// index.
outputCommitments := make(tappsbt.OutputCommitments)

// And now we commit each packet to the respective anchor output
// commitments.
for _, vPkt := range packets {
Expand Down Expand Up @@ -1544,6 +1550,26 @@ func AssertInputAnchorsEqual(packets []*tappsbt.VPacket) error {
return nil
}

// AssertInputsUnique makes sure that every input across all virtual packets is
// referencing a unique input asset, which is identified by the input PrevID.
func AssertInputsUnique(packets []*tappsbt.VPacket) error {
// PrevIDs are comparable enough to serve as a map key without hashing.
inputs := make(lfn.Set[asset.PrevID])

for _, vPkt := range packets {
for _, vIn := range vPkt.Inputs {
if inputs.Contains(vIn.PrevID) {
return fmt.Errorf("input %v is duplicated",
vIn.PrevID)
}

inputs.Add(vIn.PrevID)
}
}

return nil
}

// ExtractUnSpendable extracts all tombstones and burns from the active input
// commitment.
func ExtractUnSpendable(c *commitment.TapCommitment) []*asset.Asset {
Expand Down

0 comments on commit 7869f87

Please sign in to comment.