88 "errors"
99 "fmt"
1010 "io"
11+ "math"
1112 "reflect"
1213 "strings"
1314 "time"
@@ -22,6 +23,7 @@ import (
2223 "github.com/btcsuite/btcd/txscript"
2324 "github.com/btcsuite/btcd/wire"
2425 "github.com/lightninglabs/lndclient"
26+ "github.com/lightninglabs/taproot-assets/fn"
2527 "github.com/lightninglabs/taproot-assets/mssmt"
2628 "github.com/lightningnetwork/lnd/input"
2729 "github.com/lightningnetwork/lnd/keychain"
@@ -464,6 +466,175 @@ const (
464466 ScriptV0 ScriptVersion = 0
465467)
466468
469+ // TapscriptTreeNodes represents the two supported ways to define a tapscript
470+ // tree to be used as a sibling for a Taproot Asset commitment, an asset group
471+ // key, or an asset script key. This type is used for interfacing with the DB,
472+ // not for supplying in a proof or key derivation. The inner fields are mutually
473+ // exclusive.
474+ type TapscriptTreeNodes struct {
475+ // leaves is created from an ordered list of TapLeaf objects and
476+ // represents a Tapscript tree.
477+ leaves * TapLeafNodes
478+
479+ // branch is created from a TapBranch and represents the tapHashes of
480+ // the child nodes of a TapBranch.
481+ branch * TapBranchNodes
482+ }
483+
484+ // GetLeaves returns an Option containing a copy of the internal TapLeafNodes,
485+ // if it exists.
486+ func GetLeaves (ttn TapscriptTreeNodes ) fn.Option [TapLeafNodes ] {
487+ return fn .MaybeSome (ttn .leaves )
488+ }
489+
490+ // GetBranch returns an Option containing a copy of the internal TapBranchNodes,
491+ // if it exists.
492+ func GetBranch (ttn TapscriptTreeNodes ) fn.Option [TapBranchNodes ] {
493+ return fn .MaybeSome (ttn .branch )
494+ }
495+
496+ // FromBranch creates a TapscriptTreeNodes object from a TapBranchNodes object.
497+ func FromBranch (tbn TapBranchNodes ) TapscriptTreeNodes {
498+ return TapscriptTreeNodes {
499+ branch : & tbn ,
500+ }
501+ }
502+
503+ // FromLeaves creates a TapscriptTreeNodes object from a TapLeafNodes object.
504+ func FromLeaves (tln TapLeafNodes ) TapscriptTreeNodes {
505+ return TapscriptTreeNodes {
506+ leaves : & tln ,
507+ }
508+ }
509+
510+ // CheckTapLeafSanity asserts that a TapLeaf script is smaller than the maximum
511+ // witness size, and that the TapLeaf version is Tapscript v0.
512+ func CheckTapLeafSanity (leaf * txscript.TapLeaf ) error {
513+ if leaf == nil {
514+ return fmt .Errorf ("leaf cannot be nil" )
515+ }
516+
517+ if leaf .LeafVersion != txscript .BaseLeafVersion {
518+ return fmt .Errorf ("tapleaf version %d not supported" ,
519+ leaf .LeafVersion )
520+ }
521+
522+ if len (leaf .Script ) == 0 {
523+ return fmt .Errorf ("tapleaf script is empty" )
524+ }
525+
526+ if len (leaf .Script ) >= blockchain .MaxBlockWeight {
527+ return fmt .Errorf ("tapleaf script too large" )
528+ }
529+
530+ return nil
531+ }
532+
533+ // TapLeafNodes represents an ordered list of TapLeaf objects, that have been
534+ // checked for their script version and size. These leaves can be stored to and
535+ // loaded from the DB.
536+ type TapLeafNodes struct {
537+ v []txscript.TapLeaf
538+ }
539+
540+ // TapTreeNodesFromLeaves sanity checks an ordered list of TapLeaf objects and
541+ // constructs a TapscriptTreeNodes object if all leaves are valid.
542+ func TapTreeNodesFromLeaves (leaves []txscript.TapLeaf ) (* TapscriptTreeNodes ,
543+ error ) {
544+
545+ err := CheckTapLeavesSanity (leaves )
546+ if err != nil {
547+ return nil , err
548+ }
549+
550+ nodes := TapscriptTreeNodes {
551+ leaves : & TapLeafNodes {
552+ v : leaves ,
553+ },
554+ }
555+
556+ return & nodes , nil
557+ }
558+
559+ // CheckTapLeavesSanity asserts that a slice of TapLeafs is below the maximum
560+ // size, and that each leaf passes a sanity check for script version and size.
561+ func CheckTapLeavesSanity (leaves []txscript.TapLeaf ) error {
562+ if len (leaves ) == 0 {
563+ return fmt .Errorf ("no leaves given" )
564+ }
565+
566+ // The maximum number of leaves we will allow for a Tapscript tree we
567+ // store is 2^15 - 1. To use a larger tree, create a TapscriptTreeNodes
568+ // object from a TapBranch instead.
569+ if len (leaves ) > math .MaxInt16 {
570+ return fmt .Errorf ("tapleaf count larger than %d" ,
571+ math .MaxInt16 )
572+ }
573+
574+ // Reject any leaf not using the initial Tapscript version, or with a
575+ // script size above the maximum blocksize.
576+ for i := range leaves {
577+ err := CheckTapLeafSanity (& leaves [i ])
578+ if err != nil {
579+ return err
580+ }
581+ }
582+
583+ return nil
584+ }
585+
586+ // ToLeaves returns the TapLeaf slice inside a TapLeafNodes object.
587+ func ToLeaves (l TapLeafNodes ) []txscript.TapLeaf {
588+ return append ([]txscript.TapLeaf {}, l .v ... )
589+ }
590+
591+ // LeafNodesRootHash returns the root hash of a Tapscript tree built from the
592+ // TapLeaf nodes in a TapLeafNodes object.
593+ func LeafNodesRootHash (l TapLeafNodes ) chainhash.Hash {
594+ return txscript .AssembleTaprootScriptTree (l .v ... ).RootNode .TapHash ()
595+ }
596+
597+ // TapBranchNodesLen is the length of a TapBranch represented as a byte arrray.
598+ const TapBranchNodesLen = 64
599+
600+ // TapBranchNodes represents the tapHashes of the child nodes of a TapBranch.
601+ // These tapHashes can be stored to and loaded from the DB.
602+ type TapBranchNodes struct {
603+ left [chainhash .HashSize ]byte
604+ right [chainhash .HashSize ]byte
605+ }
606+
607+ // TapTreeNodesFromBranch creates a TapscriptTreeNodes object from a TapBranch.
608+ func TapTreeNodesFromBranch (branch txscript.TapBranch ) TapscriptTreeNodes {
609+ return TapscriptTreeNodes {
610+ branch : & TapBranchNodes {
611+ left : branch .Left ().TapHash (),
612+ right : branch .Right ().TapHash (),
613+ },
614+ }
615+ }
616+
617+ // ToBranch returns an encoded TapBranchNodes object.
618+ func ToBranch (b TapBranchNodes ) [][]byte {
619+ return EncodeTapBranchNodes (b )
620+ }
621+
622+ // BranchNodesRootHash returns the root hash of a Tapscript tree built from the
623+ // tapHashes stored in a TapBranchNodes object.
624+ func BranchNodesRootHash (b TapBranchNodes ) chainhash.Hash {
625+ return NewTapBranchHash (b .left , b .right )
626+ }
627+
628+ // NewTapBranchHash takes the raw tap hashes of the left and right nodes and
629+ // hashes them into a branch.
630+ func NewTapBranchHash (l , r chainhash.Hash ) chainhash.Hash {
631+ if bytes .Compare (l [:], r [:]) > 0 {
632+ l , r = r , l
633+ }
634+
635+ return * chainhash .TaggedHash (chainhash .TagTapBranch , l [:], r [:])
636+ }
637+
467638// AssetGroup holds information about an asset group, including the genesis
468639// information needed re-tweak the raw key.
469640type AssetGroup struct {
0 commit comments