@@ -106,6 +106,9 @@ var (
106106 // asset split leaves.
107107 ZeroPrevID PrevID
108108
109+ // EmptyGenesis is the empty Genesis struct used for alt leaves.
110+ EmptyGenesis Genesis
111+
109112 // NUMSBytes is the NUMs point we'll use for un-spendable script keys.
110113 // It was generated via a try-and-increment approach using the phrase
111114 // "taproot-assets" with SHA2-256. The code for the try-and-increment
@@ -2238,3 +2241,137 @@ type ChainAsset struct {
22382241 // available for coin selection.
22392242 AnchorLeaseExpiry * time.Time
22402243}
2244+
2245+ // An AltLeaf is a type that is used to carry arbitrary data, and does not
2246+ // represent a Taproot asset. An AltLeaf can be used to anchor other protocols
2247+ // alongside Taproot Asset transactions.
2248+ type AltLeaf [T any ] interface {
2249+ // Copyable asserts that the target type of this interface satisfies
2250+ // the Copyable interface.
2251+ fn.Copyable [T ]
2252+
2253+ // ValidateAltLeaf ensures that an AltLeaf is valid.
2254+ ValidateAltLeaf () error
2255+
2256+ // EncodeAltLeaf encodes an AltLeaf into a TLV stream.
2257+ EncodeAltLeaf (w io.Writer ) error
2258+
2259+ // DecodeAltLeaf decodes an AltLeaf from a TLV stream.
2260+ DecodeAltLeaf (r io.Reader ) error
2261+ }
2262+
2263+ // NewAltLeaf instantiates a new valid AltLeaf.
2264+ func NewAltLeaf (key ScriptKey , keyVersion ScriptVersion ,
2265+ prevWitness []Witness ) (* Asset , error ) {
2266+
2267+ if key .PubKey == nil {
2268+ return nil , fmt .Errorf ("script key must be non-nil" )
2269+ }
2270+
2271+ return & Asset {
2272+ Version : V0 ,
2273+ Genesis : EmptyGenesis ,
2274+ Amount : 0 ,
2275+ LockTime : 0 ,
2276+ RelativeLockTime : 0 ,
2277+ PrevWitnesses : prevWitness ,
2278+ SplitCommitmentRoot : nil ,
2279+ GroupKey : nil ,
2280+ ScriptKey : key ,
2281+ ScriptVersion : keyVersion ,
2282+ }, nil
2283+ }
2284+
2285+ // CopyAltLeaf performs a deep copy of an AltLeaf.
2286+ func CopyAltLeaf [T AltLeaf [T ]](a AltLeaf [T ]) AltLeaf [T ] {
2287+ return a .Copy ()
2288+ }
2289+
2290+ // CopyAltLeaves performs a deep copy of an AltLeaf slice.
2291+ func CopyAltLeaves [T AltLeaf [T ]](a []AltLeaf [T ]) []AltLeaf [T ] {
2292+ return fn .Map (a , CopyAltLeaf [T ])
2293+ }
2294+
2295+ // Validate checks that an Asset is a valid AltLeaf. An Asset used as an AltLeaf
2296+ // must meet these constraints:
2297+ // - Version must be V0.
2298+ // - Genesis must be the empty Genesis.
2299+ // - Amount, LockTime, and RelativeLockTime must be 0.
2300+ // - SplitCommitmentRoot and GroupKey must be nil.
2301+ // - ScriptKey must be non-nil.
2302+ func (a * Asset ) ValidateAltLeaf () error {
2303+ if a .Version != V0 {
2304+ return fmt .Errorf ("alt leaf version must be 0" )
2305+ }
2306+
2307+ if a .Genesis != EmptyGenesis {
2308+ return fmt .Errorf ("alt leaf genesis must be the empty genesis" )
2309+ }
2310+
2311+ if a .Amount != 0 {
2312+ return fmt .Errorf ("alt leaf amount must be 0" )
2313+ }
2314+
2315+ if a .LockTime != 0 {
2316+ return fmt .Errorf ("alt leaf lock time must be 0" )
2317+ }
2318+
2319+ if a .RelativeLockTime != 0 {
2320+ return fmt .Errorf ("alt leaf relative lock time must be 0" )
2321+ }
2322+
2323+ if a .SplitCommitmentRoot != nil {
2324+ return fmt .Errorf (
2325+ "alt leaf split commitment root must be empty" ,
2326+ )
2327+ }
2328+
2329+ if a .GroupKey != nil {
2330+ return fmt .Errorf ("alt leaf group key must be empty" )
2331+ }
2332+
2333+ if a .ScriptKey .PubKey == nil {
2334+ return fmt .Errorf ("alt leaf script key must be non-nil" )
2335+ }
2336+
2337+ return nil
2338+ }
2339+
2340+ // encodeAltLeafRecords determines the set of non-nil records to include when
2341+ // encoding an AltLeaf. Since the Genesis, Group Key, Amount, and Version fields
2342+ // are static, we can omit those fields.
2343+ func (a * Asset ) encodeAltLeafRecords () []tlv.Record {
2344+ records := make ([]tlv.Record , 0 , 3 )
2345+
2346+ // Always use the normal witness encoding, since the asset version is
2347+ // always V0.
2348+ if len (a .PrevWitnesses ) > 0 {
2349+ records = append (records , NewLeafPrevWitnessRecord (
2350+ & a .PrevWitnesses , EncodeNormal ,
2351+ ))
2352+ }
2353+ records = append (records , NewLeafScriptVersionRecord (& a .ScriptVersion ))
2354+ records = append (records , NewLeafScriptKeyRecord (& a .ScriptKey .PubKey ))
2355+
2356+ // Add any unknown odd types that were encountered during decoding.
2357+ return CombineRecords (records , a .UnknownOddTypes )
2358+ }
2359+
2360+ // EncodeAltLeaf encodes an AltLeaf into a TLV stream.
2361+ func (a * Asset ) EncodeAltLeaf (w io.Writer ) error {
2362+ stream , err := tlv .NewStream (a .encodeAltLeafRecords ()... )
2363+ if err != nil {
2364+ return err
2365+ }
2366+ return stream .Encode (w )
2367+ }
2368+
2369+ // DecodeAltLeaf decodes an AltLeaf from a TLV stream. The normal Asset decoder
2370+ // can be reused here, since any Asset field not encoded in the AltLeaf will
2371+ // be set to its default value, which matches the AltLeaf validity constraints.
2372+ func (a * Asset ) DecodeAltLeaf (r io.Reader ) error {
2373+ return a .Decode (r )
2374+ }
2375+
2376+ // Ensure Asset implements the AltLeaf interface.
2377+ var _ AltLeaf [* Asset ] = (* Asset )(nil )
0 commit comments