@@ -110,6 +110,9 @@ var (
110
110
// EmptyGenesis is the empty Genesis struct used for alt leaves.
111
111
EmptyGenesis Genesis
112
112
113
+ // EmptyGenesisID is the ID of the empty genesis struct.
114
+ EmptyGenesisID = EmptyGenesis .ID ()
115
+
113
116
// NUMSBytes is the NUMs point we'll use for un-spendable script keys.
114
117
// It was generated via a try-and-increment approach using the phrase
115
118
// "taproot-assets" with SHA2-256. The code for the try-and-increment
@@ -128,6 +131,14 @@ var (
128
131
// ErrUnknownVersion is returned when an asset with an unknown asset
129
132
// version is being used.
130
133
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" )
131
142
)
132
143
133
144
const (
@@ -241,12 +252,6 @@ func DecodeGenesis(r io.Reader) (Genesis, error) {
241
252
return gen , err
242
253
}
243
254
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
-
250
255
// Specifier is a type that can be used to specify an asset by its ID, its asset
251
256
// group public key, or both.
252
257
type Specifier struct {
@@ -2794,13 +2799,25 @@ type ChainAsset struct {
2794
2799
AnchorLeaseExpiry * time.Time
2795
2800
}
2796
2801
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
+
2797
2810
// An AltLeaf is a type that is used to carry arbitrary data, and does not
2798
2811
// represent a Taproot asset. An AltLeaf can be used to anchor other protocols
2799
2812
// alongside Taproot Asset transactions.
2800
2813
type AltLeaf [T any ] interface {
2801
2814
// Copyable asserts that the target type of this interface satisfies
2802
2815
// 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
2804
2821
2805
2822
// ValidateAltLeaf ensures that an AltLeaf is valid.
2806
2823
ValidateAltLeaf () error
@@ -2834,18 +2851,17 @@ func NewAltLeaf(key ScriptKey, keyVersion ScriptVersion,
2834
2851
}, nil
2835
2852
}
2836
2853
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
-
2842
2854
// 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 )))
2845
2861
}
2846
2862
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:
2849
2865
// - Version must be V0.
2850
2866
// - Genesis must be the empty Genesis.
2851
2867
// - Amount, LockTime, and RelativeLockTime must be 0.
@@ -2873,9 +2889,8 @@ func (a *Asset) ValidateAltLeaf() error {
2873
2889
}
2874
2890
2875
2891
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" )
2879
2894
}
2880
2895
2881
2896
if a .GroupKey != nil {
@@ -2889,6 +2904,45 @@ func (a *Asset) ValidateAltLeaf() error {
2889
2904
return nil
2890
2905
}
2891
2906
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
+
2892
2946
// encodeAltLeafRecords determines the set of non-nil records to include when
2893
2947
// encoding an AltLeaf. Since the Genesis, Group Key, Amount, and Version fields
2894
2948
// are static, we can omit those fields.
@@ -2926,4 +2980,19 @@ func (a *Asset) DecodeAltLeaf(r io.Reader) error {
2926
2980
}
2927
2981
2928
2982
// 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