@@ -109,6 +109,9 @@ var (
109109 // EmptyGenesis is the empty Genesis struct used for alt leaves.
110110 EmptyGenesis Genesis
111111
112+ // EmptyGenesisID is the ID of the empty genesis struct.
113+ EmptyGenesisID = EmptyGenesis .ID ()
114+
112115 // NUMSBytes is the NUMs point we'll use for un-spendable script keys.
113116 // It was generated via a try-and-increment approach using the phrase
114117 // "taproot-assets" with SHA2-256. The code for the try-and-increment
@@ -127,6 +130,14 @@ var (
127130 // ErrUnknownVersion is returned when an asset with an unknown asset
128131 // version is being used.
129132 ErrUnknownVersion = errors .New ("asset: unknown asset version" )
133+
134+ // ErrUnwrapAssetID is returned when an asset ID cannot be unwrapped
135+ // from a Specifier.
136+ ErrUnwrapAssetID = errors .New ("unable to unwrap asset ID" )
137+
138+ // ErrDuplicateAltLeafKey is returned when a slice of AltLeaves contains
139+ // 2 or more AltLeaves with the same AssetCommitmentKey.
140+ ErrDuplicateAltLeafKey = errors .New ("duplicate alt leaf key" )
130141)
131142
132143const (
@@ -250,12 +261,6 @@ func DecodeGenesis(r io.Reader) (Genesis, error) {
250261 return gen , err
251262}
252263
253- var (
254- // ErrUnwrapAssetID is an error type which is returned when an asset ID
255- // cannot be unwrapped from a specifier.
256- ErrUnwrapAssetID = errors .New ("unable to unwrap asset ID" )
257- )
258-
259264// Specifier is a type that can be used to specify an asset by its ID, its asset
260265// group public key, or both.
261266type Specifier struct {
@@ -2296,6 +2301,14 @@ type ChainAsset struct {
22962301 AnchorLeaseExpiry * time.Time
22972302}
22982303
2304+ // LeafKeySet is a set of leaf keys.
2305+ type LeafKeySet = fn.Set [[32 ]byte ]
2306+
2307+ // NewLeafKeySet creates a new leaf key set.
2308+ func NewLeafKeySet () LeafKeySet {
2309+ return fn .NewSet [[32 ]byte ]()
2310+ }
2311+
22992312// An AltLeaf is a type that is used to carry arbitrary data, and does not
23002313// represent a Taproot asset. An AltLeaf can be used to anchor other protocols
23012314// alongside Taproot Asset transactions.
@@ -2304,6 +2317,10 @@ type AltLeaf[T any] interface {
23042317 // the Copyable interface.
23052318 fn.Copyable [* T ]
23062319
2320+ // AssetCommitmentKey is the key for an AltLeaf within an
2321+ // AssetCommitment.
2322+ AssetCommitmentKey () [32 ]byte
2323+
23072324 // ValidateAltLeaf ensures that an AltLeaf is valid.
23082325 ValidateAltLeaf () error
23092326
@@ -2389,6 +2406,45 @@ func (a *Asset) ValidateAltLeaf() error {
23892406 return nil
23902407}
23912408
2409+ // ValidAltLeaves checks that a set of Assets are valid AltLeaves, and can be
2410+ // used to construct an AltCommitment. This requires that each AltLeaf has a
2411+ // unique AssetCommitmentKey.
2412+ func ValidAltLeaves (leaves []AltLeaf [Asset ]) error {
2413+ leafKeys := NewLeafKeySet ()
2414+ return AddLeafKeysVerifyUnique (leafKeys , leaves )
2415+ }
2416+
2417+ // AddLeafKeysVerifyUnique checks that a set of Assets are valid AltLeaves, and
2418+ // have unique AssetCommitmentKeys (unique among the given slice but also not
2419+ // colliding with any of the keys in the existingKeys set). If the leaves are
2420+ // valid, the function returns the updated set of keys.
2421+ func AddLeafKeysVerifyUnique (existingKeys LeafKeySet ,
2422+ leaves []AltLeaf [Asset ]) error {
2423+
2424+ for _ , leaf := range leaves {
2425+ err := leaf .ValidateAltLeaf ()
2426+ if err != nil {
2427+ return err
2428+ }
2429+
2430+ leafKey := leaf .AssetCommitmentKey ()
2431+ if existingKeys .Contains (leafKey ) {
2432+ return fmt .Errorf ("%w: %x" , ErrDuplicateAltLeafKey ,
2433+ leafKey )
2434+ }
2435+
2436+ existingKeys .Add (leafKey )
2437+ }
2438+
2439+ return nil
2440+ }
2441+
2442+ // IsAltLeaf returns true if an Asset would be stored in the AltCommitment of
2443+ // a TapCommitment. It does not check if the Asset is a valid AltLeaf.
2444+ func (a * Asset ) IsAltLeaf () bool {
2445+ return a .GroupKey == nil && a .Genesis == EmptyGenesis
2446+ }
2447+
23922448// encodeAltLeafRecords determines the set of non-nil records to include when
23932449// encoding an AltLeaf. Since the Genesis, Group Key, Amount, and Version fields
23942450// are static, we can omit those fields.
0 commit comments