@@ -1044,6 +1044,9 @@ func CreateOutputCommitments(
10441044 if err := AssertOutputAnchorsEqual (packets ); err != nil {
10451045 return nil , err
10461046 }
1047+ if err := AssertOutputAltLeavesValid (packets ); err != nil {
1048+ return nil , err
1049+ }
10471050
10481051 // We need to make sure this transaction doesn't lead to collisions in
10491052 // any of the trees (e.g. two asset outputs with the same script key in
@@ -1125,6 +1128,13 @@ func commitPacket(vPkt *tappsbt.VPacket,
11251128 err )
11261129 }
11271130
1131+ // If the vOutput contains any AltLeaves, merge them into the
1132+ // tap commitment.
1133+ err = sendTapCommitment .MergeAltLeaves (vOut .AltLeaves )
1134+ if err != nil {
1135+ return fmt .Errorf ("error merging alt leaves: %w" , err )
1136+ }
1137+
11281138 // Merge the finished TAP level commitment with the existing
11291139 // one (if any) for the anchor output.
11301140 anchorOutputCommitment , ok := outputCommitments [anchorOutputIdx ]
@@ -1570,6 +1580,37 @@ func AssertInputsUnique(packets []*tappsbt.VPacket) error {
15701580 return nil
15711581}
15721582
1583+ // AssertOutputAltLeavesValid checks that, for each anchor output, the AltLeaves
1584+ // carried by all packets assigned to that output can be used to construct an
1585+ // AltCommitment.
1586+ func AssertOutputAltLeavesValid (vPackets []* tappsbt.VPacket ) error {
1587+ anchorLeaves := make (map [uint32 ]fn.Set [[32 ]byte ])
1588+
1589+ for _ , pkt := range vPackets {
1590+ for _ , vOut := range pkt .Outputs {
1591+ if len (vOut .AltLeaves ) == 0 {
1592+ continue
1593+ }
1594+
1595+ // Build a set of AltLeaf keys for each anchor output.
1596+ outIndex := vOut .AnchorOutputIndex
1597+ if _ , ok := anchorLeaves [outIndex ]; ! ok {
1598+ anchorLeaves [outIndex ] = fn .NewSet [[32 ]byte ]()
1599+ }
1600+
1601+ err := asset .AddLeafKeysVerifyUnique (
1602+ anchorLeaves [outIndex ], vOut .AltLeaves ,
1603+ )
1604+ if err != nil {
1605+ return fmt .Errorf ("anchor output %d: %w" ,
1606+ outIndex , err )
1607+ }
1608+ }
1609+ }
1610+
1611+ return nil
1612+ }
1613+
15731614// ExtractUnSpendable extracts all tombstones and burns from the active input
15741615// commitment.
15751616func ExtractUnSpendable (c * commitment.TapCommitment ) []* asset.Asset {
0 commit comments