@@ -110,6 +110,9 @@ var (
110110 // EmptyGenesis is the empty Genesis struct used for alt leaves.
111111 EmptyGenesis Genesis
112112
113+ // EmptyGenesisID is the ID of the empty genesis struct.
114+ EmptyGenesisID = EmptyGenesis .ID ()
115+
113116 // NUMSBytes is the NUMs point we'll use for un-spendable script keys.
114117 // It was generated via a try-and-increment approach using the phrase
115118 // "taproot-assets" with SHA2-256. The code for the try-and-increment
@@ -128,6 +131,14 @@ var (
128131 // ErrUnknownVersion is returned when an asset with an unknown asset
129132 // version is being used.
130133 ErrUnknownVersion = errors .New ("asset: unknown asset version" )
134+
135+ // ErrUnwrapAssetID is returned when an asset ID cannot be unwrapped
136+ // from a Specifier.
137+ ErrUnwrapAssetID = errors .New ("unable to unwrap asset ID" )
138+
139+ // ErrDuplicateAltLeafKey is returned when a slice of AltLeaves contains
140+ // 2 or more AltLeaves with the same AssetCommitmentKey.
141+ ErrDuplicateAltLeafKey = errors .New ("duplicate alt leaf key" )
131142)
132143
133144const (
@@ -241,12 +252,6 @@ func DecodeGenesis(r io.Reader) (Genesis, error) {
241252 return gen , err
242253}
243254
244- var (
245- // ErrUnwrapAssetID is an error type which is returned when an asset ID
246- // cannot be unwrapped from a specifier.
247- ErrUnwrapAssetID = errors .New ("unable to unwrap asset ID" )
248- )
249-
250255// Specifier is a type that can be used to specify an asset by its ID, its asset
251256// group public key, or both.
252257type Specifier struct {
@@ -2794,13 +2799,25 @@ type ChainAsset struct {
27942799 AnchorLeaseExpiry * time.Time
27952800}
27962801
2802+ // LeafKeySet is a set of leaf keys.
2803+ type LeafKeySet = fn.Set [[32 ]byte ]
2804+
2805+ // NewLeafKeySet creates a new leaf key set.
2806+ func NewLeafKeySet () LeafKeySet {
2807+ return fn .NewSet [[32 ]byte ]()
2808+ }
2809+
27972810// An AltLeaf is a type that is used to carry arbitrary data, and does not
27982811// represent a Taproot asset. An AltLeaf can be used to anchor other protocols
27992812// alongside Taproot Asset transactions.
28002813type AltLeaf [T any ] interface {
28012814 // Copyable asserts that the target type of this interface satisfies
28022815 // the Copyable interface.
2803- fn.Copyable [T ]
2816+ fn.Copyable [* T ]
2817+
2818+ // AssetCommitmentKey is the key for an AltLeaf within an
2819+ // AssetCommitment.
2820+ AssetCommitmentKey () [32 ]byte
28042821
28052822 // ValidateAltLeaf ensures that an AltLeaf is valid.
28062823 ValidateAltLeaf () error
@@ -2834,18 +2851,17 @@ func NewAltLeaf(key ScriptKey, keyVersion ScriptVersion,
28342851 }, nil
28352852}
28362853
2837- // CopyAltLeaf performs a deep copy of an AltLeaf.
2838- func CopyAltLeaf [T AltLeaf [T ]](a AltLeaf [T ]) AltLeaf [T ] {
2839- return a .Copy ()
2840- }
2841-
28422854// CopyAltLeaves performs a deep copy of an AltLeaf slice.
2843- func CopyAltLeaves [T AltLeaf [T ]](a []AltLeaf [T ]) []AltLeaf [T ] {
2844- return fn .Map (a , CopyAltLeaf [T ])
2855+ func CopyAltLeaves (a []AltLeaf [Asset ]) []AltLeaf [Asset ] {
2856+ if len (a ) == 0 {
2857+ return nil
2858+ }
2859+
2860+ return ToAltLeaves (fn .CopyAll (FromAltLeaves (a )))
28452861}
28462862
2847- // Validate checks that an Asset is a valid AltLeaf. An Asset used as an AltLeaf
2848- // must meet these constraints:
2863+ // ValidateAltLeaf checks that an Asset is a valid AltLeaf. An Asset used as an
2864+ // AltLeaf must meet these constraints:
28492865// - Version must be V0.
28502866// - Genesis must be the empty Genesis.
28512867// - Amount, LockTime, and RelativeLockTime must be 0.
@@ -2873,9 +2889,8 @@ func (a *Asset) ValidateAltLeaf() error {
28732889 }
28742890
28752891 if a .SplitCommitmentRoot != nil {
2876- return fmt .Errorf (
2877- "alt leaf split commitment root must be empty" ,
2878- )
2892+ return fmt .Errorf ("alt leaf split commitment root must be " +
2893+ "empty" )
28792894 }
28802895
28812896 if a .GroupKey != nil {
@@ -2889,6 +2904,45 @@ func (a *Asset) ValidateAltLeaf() error {
28892904 return nil
28902905}
28912906
2907+ // ValidAltLeaves checks that a set of Assets are valid AltLeaves, and can be
2908+ // used to construct an AltCommitment. This requires that each AltLeaf has a
2909+ // unique AssetCommitmentKey.
2910+ func ValidAltLeaves (leaves []AltLeaf [Asset ]) error {
2911+ leafKeys := NewLeafKeySet ()
2912+ return AddLeafKeysVerifyUnique (leafKeys , leaves )
2913+ }
2914+
2915+ // AddLeafKeysVerifyUnique checks that a set of Assets are valid AltLeaves, and
2916+ // have unique AssetCommitmentKeys (unique among the given slice but also not
2917+ // colliding with any of the keys in the existingKeys set). If the leaves are
2918+ // valid, the function returns the updated set of keys.
2919+ func AddLeafKeysVerifyUnique (existingKeys LeafKeySet ,
2920+ leaves []AltLeaf [Asset ]) error {
2921+
2922+ for _ , leaf := range leaves {
2923+ err := leaf .ValidateAltLeaf ()
2924+ if err != nil {
2925+ return err
2926+ }
2927+
2928+ leafKey := leaf .AssetCommitmentKey ()
2929+ if existingKeys .Contains (leafKey ) {
2930+ return fmt .Errorf ("%w: %x" , ErrDuplicateAltLeafKey ,
2931+ leafKey )
2932+ }
2933+
2934+ existingKeys .Add (leafKey )
2935+ }
2936+
2937+ return nil
2938+ }
2939+
2940+ // IsAltLeaf returns true if an Asset would be stored in the AltCommitment of
2941+ // a TapCommitment. It does not check if the Asset is a valid AltLeaf.
2942+ func (a * Asset ) IsAltLeaf () bool {
2943+ return a .GroupKey == nil && a .Genesis == EmptyGenesis
2944+ }
2945+
28922946// encodeAltLeafRecords determines the set of non-nil records to include when
28932947// encoding an AltLeaf. Since the Genesis, Group Key, Amount, and Version fields
28942948// are static, we can omit those fields.
@@ -2926,4 +2980,19 @@ func (a *Asset) DecodeAltLeaf(r io.Reader) error {
29262980}
29272981
29282982// Ensure Asset implements the AltLeaf interface.
2929- var _ AltLeaf [* Asset ] = (* Asset )(nil )
2983+ var _ AltLeaf [Asset ] = (* Asset )(nil )
2984+
2985+ // ToAltLeaves casts []Asset to []AltLeafAsset, without checking that the assets
2986+ // are valid AltLeaves.
2987+ func ToAltLeaves (leaves []* Asset ) []AltLeaf [Asset ] {
2988+ return fn .Map (leaves , func (l * Asset ) AltLeaf [Asset ] {
2989+ return l
2990+ })
2991+ }
2992+
2993+ // FromAltLeaves casts []AltLeafAsset to []Asset, which is always safe.
2994+ func FromAltLeaves (leaves []AltLeaf [Asset ]) []* Asset {
2995+ return fn .Map (leaves , func (l AltLeaf [Asset ]) * Asset {
2996+ return l .(* Asset )
2997+ })
2998+ }
0 commit comments