8
8
"errors"
9
9
"fmt"
10
10
"io"
11
+ "math"
11
12
"reflect"
12
13
"strings"
13
14
"time"
@@ -22,6 +23,7 @@ import (
22
23
"github.com/btcsuite/btcd/txscript"
23
24
"github.com/btcsuite/btcd/wire"
24
25
"github.com/lightninglabs/lndclient"
26
+ "github.com/lightninglabs/taproot-assets/fn"
25
27
"github.com/lightninglabs/taproot-assets/mssmt"
26
28
"github.com/lightningnetwork/lnd/input"
27
29
"github.com/lightningnetwork/lnd/keychain"
@@ -464,6 +466,175 @@ const (
464
466
ScriptV0 ScriptVersion = 0
465
467
)
466
468
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
+
467
638
// AssetGroup holds information about an asset group, including the genesis
468
639
// information needed re-tweak the raw key.
469
640
type AssetGroup struct {
0 commit comments