88 "github.com/btcsuite/btcd/chaincfg/chainhash"
99 "github.com/btcsuite/btcd/txscript"
1010 "github.com/btcsuite/btcd/wire"
11+ "github.com/lightninglabs/taproot-assets/asset"
12+ "github.com/lightninglabs/taproot-assets/fn"
1113)
1214
1315const (
3739 "invalid tapscript preimage length" ,
3840 )
3941
42+ // ErrInvalidTapscriptPreimageType is an error returned when a tapscript
43+ // preimage has an unknown type that is not leaf nor branch.
44+ ErrInvalidTapscriptPreimageType = errors .New (
45+ "unknown tapscript preimage type" ,
46+ )
47+
4048 // ErrPreimageIsTapCommitment is an error returned when a tapscript
4149 // preimage is a valid Taproot Asset commitment.
4250 ErrPreimageIsTapCommitment = errors .New (
@@ -66,51 +74,152 @@ func (t TapscriptPreimageType) String() string {
6674 return "BranchPreimage"
6775
6876 default :
69- return fmt .Sprintf ("UnKnownSiblingType (%d)" , t )
77+ return fmt .Sprintf ("UnknownSiblingType (%d)" , t )
7078 }
7179}
7280
7381// TapscriptPreimage wraps a pre-image byte slice with a type byte that self
7482// identifies what type of pre-image it is.
7583type TapscriptPreimage struct {
76- // SiblingPreimage is the pre-image itself. This will be either 32 or
77- // 64 bytes.
78- SiblingPreimage []byte
84+ // SiblingPreimage is the pre-image itself. This will be 64 bytes if
85+ // representing a TapBranch, or any size under 4 MBytes if representing
86+ // a TapLeaf.
87+ siblingPreimage []byte
7988
8089 // SiblingType is the type of the pre-image.
81- SiblingType TapscriptPreimageType
90+ siblingType TapscriptPreimageType
8291}
8392
8493// NewPreimageFromLeaf creates a new TapscriptPreimage from a single tap leaf.
85- func NewPreimageFromLeaf (leaf txscript.TapLeaf ) * TapscriptPreimage {
86- // The leaf encoding is: leafVersion || compactSizeof(script) ||
87- // script, where compactSizeof returns the compact size needed to
88- // encode the value.
94+ func NewPreimageFromLeaf (leaf txscript.TapLeaf ) (* TapscriptPreimage , error ) {
95+ // Check the leaf size and version, and assert that the leaf script is
96+ // not a Taproot Asset Commitment.
97+ err := asset .CheckTapLeafSanity (& leaf )
98+ if err != nil {
99+ return nil , err
100+ }
101+
102+ if IsTaprootAssetCommitmentScript (leaf .Script ) {
103+ return nil , ErrPreimageIsTapCommitment
104+ }
105+
106+ // The leaf encoding is: leafVersion || compactSizeof(script) || script,
107+ // where compactSizeof returns the compact size needed to encode the
108+ // value.
89109 var encodedLeaf bytes.Buffer
90110
91111 _ = encodedLeaf .WriteByte (byte (leaf .LeafVersion ))
92112 _ = wire .WriteVarBytes (& encodedLeaf , 0 , leaf .Script )
93113
94114 return & TapscriptPreimage {
95- SiblingPreimage : encodedLeaf .Bytes (),
96- SiblingType : LeafPreimage ,
115+ siblingPreimage : encodedLeaf .Bytes (),
116+ siblingType : LeafPreimage ,
117+ }, nil
118+ }
119+
120+ // NewLeafFromPreimage sanity checks a TapscriptPreimage and decodes it into a
121+ // TapLeaf.
122+ func NewLeafFromPreimage (preimage TapscriptPreimage ) (* txscript.TapLeaf ,
123+ error ) {
124+
125+ // The preimage must be a TapLeaf.
126+ if preimage .Type () != LeafPreimage {
127+ return nil , ErrInvalidTapscriptPreimageType
128+ }
129+
130+ // Remove the leaf version and script size prefix from the preimage.
131+ // The prefix is at least 2 bytes long, and if it's missing then this
132+ // preimage was not created correctly.
133+ if len (preimage .siblingPreimage ) < 2 {
134+ return nil , ErrInvalidTapscriptPreimageLen
135+ }
136+
137+ // The script is encoded with a leading VarByte that indicates its total
138+ // length.
139+ version := txscript .TapscriptLeafVersion (preimage .siblingPreimage [0 ])
140+ remaining := preimage .siblingPreimage [1 :]
141+ script , err := wire .ReadVarBytes (
142+ bytes .NewReader (remaining ), 0 , uint32 (len (remaining )), "script" ,
143+ )
144+ if err != nil {
145+ return nil , fmt .Errorf ("error decoding leaf pre-image: %w" , err )
97146 }
147+
148+ // The script must not be a Taproot Asset Commitment.
149+ if IsTaprootAssetCommitmentScript (script ) {
150+ return nil , ErrPreimageIsTapCommitment
151+ }
152+
153+ return fn .Ptr (txscript .NewTapLeaf (version , script )), nil
98154}
99155
100156// NewPreimageFromBranch creates a new TapscriptPreimage from a tap branch.
101- func NewPreimageFromBranch (branch txscript.TapBranch ) * TapscriptPreimage {
157+ func NewPreimageFromBranch (branch txscript.TapBranch ) TapscriptPreimage {
158+ leftHash := branch .Left ().TapHash ()
159+ rightHash := branch .Right ().TapHash ()
160+ branchBytes := bytes .Join ([][]byte {leftHash [:], rightHash [:]}, nil )
161+
162+ return TapscriptPreimage {
163+ siblingPreimage : branchBytes ,
164+ siblingType : BranchPreimage ,
165+ }
166+ }
167+
168+ // TapTreeToSibling constucts a taproot sibling hash from Tapscript tree nodes,
169+ // to be used with a TapCommitment tree root to derive a tapscript root. This
170+ // could be multiple TapLeaf objects, or a representation of a TapBranch.
171+ func NewPreimageFromTapscriptTreeNodes (
172+ tn asset.TapscriptTreeNodes ) (* TapscriptPreimage , error ) {
173+
102174 var (
103- encodedBranch bytes.Buffer
104- leftHash = branch .Left ().TapHash ()
105- rightHash = branch .Right ().TapHash ()
175+ preimage * TapscriptPreimage
176+ preimageErr error
106177 )
107- _ , _ = encodedBranch .Write (leftHash [:])
108- _ , _ = encodedBranch .Write (rightHash [:])
109178
110- return & TapscriptPreimage {
111- SiblingPreimage : encodedBranch .Bytes (),
112- SiblingType : BranchPreimage ,
179+ asset .GetLeaves (tn ).WhenSome (func (tln asset.TapLeafNodes ) {
180+ leaves := asset .ToLeaves (tln )
181+
182+ // Check that none of the leaves are a Taproot Asset Commitment.
183+ badLeaves := fn .Any (leaves , func (leaf txscript.TapLeaf ) bool {
184+ return IsTaprootAssetCommitmentScript (leaf .Script )
185+ })
186+ if badLeaves {
187+ preimageErr = ErrPreimageIsTapCommitment
188+ return
189+ }
190+
191+ switch len (leaves ) {
192+ case 1 :
193+ // If we only have one leaf, our preimage is just the
194+ // encoded leaf.
195+ preimage , preimageErr = NewPreimageFromLeaf (leaves [0 ])
196+
197+ default :
198+ // Make a branch from the leaves.
199+ tree := txscript .AssembleTaprootScriptTree (leaves ... )
200+ branch := txscript .NewTapBranch (
201+ tree .RootNode .Left (), tree .RootNode .Right (),
202+ )
203+ preimage = fn .Ptr (NewPreimageFromBranch (branch ))
204+ }
205+ })
206+
207+ asset .GetBranch (tn ).WhenSome (func (tbn asset.TapBranchNodes ) {
208+ branch := asset .ToBranch (tbn )
209+ preimage = & TapscriptPreimage {
210+ siblingPreimage : bytes .Join (branch , nil ),
211+ siblingType : BranchPreimage ,
212+ }
213+ })
214+
215+ if preimageErr != nil {
216+ return nil , preimageErr
113217 }
218+ if preimage == nil {
219+ return nil , fmt .Errorf ("malformed tapscript tree nodes" )
220+ }
221+
222+ return preimage , nil
114223}
115224
116225// IsEmpty returns true if the sibling pre-image is empty.
@@ -119,7 +228,12 @@ func (t *TapscriptPreimage) IsEmpty() bool {
119228 return true
120229 }
121230
122- return len (t .SiblingPreimage ) == 0
231+ return len (t .siblingPreimage ) == 0
232+ }
233+
234+ // Type returns the preimage type.
235+ func (t * TapscriptPreimage ) Type () TapscriptPreimageType {
236+ return TapscriptPreimageType (t .siblingType )
123237}
124238
125239// TapHash returns the tap hash of this preimage according to its type.
@@ -128,33 +242,35 @@ func (t *TapscriptPreimage) TapHash() (*chainhash.Hash, error) {
128242 return nil , ErrInvalidEmptyTapscriptPreimage
129243 }
130244
131- switch t .SiblingType {
132- // The sibling is actually a leaf pre-image, so we'll verify that it
133- // isn't a Taproot Asset commitment, and then hash it with the
134- // commitment to obtain our root.
245+ switch t .siblingType {
246+ // The sibling is a leaf pre-image, so we'll verify that it isn't a
247+ // Taproot Asset commitment, and then compute its TapHash.
135248 case LeafPreimage :
136- return TapLeafHash (t .SiblingPreimage )
249+ leaf , err := NewLeafFromPreimage (* t )
250+ if err != nil {
251+ return nil , err
252+ }
137253
138- // The sibling is actually a branch pre-image, so we'll verify that the
139- // branch pre-image is 64-bytes (the two 32-byte hashes of the left
140- // and right nodes), and then derive the key from that.
254+ return fn .Ptr (leaf .TapHash ()), nil
255+
256+ // The sibling is a branch pre-image, so we'll verify that the pre-image
257+ // is 64-bytes (the two 32-byte hashes of the left and right nodes),
258+ // and then derive the TapHash from that.
141259 case BranchPreimage :
142- return TapBranchHash (t .SiblingPreimage )
260+ if len (t .siblingPreimage ) != tapBranchPreimageLen {
261+ return nil , ErrInvalidTapscriptPreimageLen
262+ }
143263
144- default :
145- return nil , fmt .Errorf ("unknown sibling type: <%d>" ,
146- t .SiblingType )
147- }
148- }
264+ var left , right chainhash.Hash
265+ left = (chainhash .Hash )(t .siblingPreimage [:chainhash .HashSize ])
266+ right = (chainhash .Hash )(t .siblingPreimage [chainhash .HashSize :])
149267
150- // VerifyNoCommitment verifies that the preimage is not a Taproot Asset
151- // commitment.
152- func (t * TapscriptPreimage ) VerifyNoCommitment () error {
153- if IsTaprootAssetCommitmentScript (t .SiblingPreimage ) {
154- return ErrPreimageIsTapCommitment
155- }
268+ return fn .Ptr (asset .NewTapBranchHash (left , right )), nil
156269
157- return nil
270+ default :
271+ return nil , fmt .Errorf ("%w: %d" ,
272+ ErrInvalidTapscriptPreimageType , t .siblingType )
273+ }
158274}
159275
160276// MaybeDecodeTapscriptPreimage returns the decoded preimage and hash of the
@@ -179,6 +295,8 @@ func MaybeDecodeTapscriptPreimage(encoded []byte) (*TapscriptPreimage,
179295 "preimage: %w" , err )
180296 }
181297
298+ // Validate the correctness of the decoded preimage and compute its
299+ // TapHash.
182300 tapHash , err := preimage .TapHash ()
183301 if err != nil {
184302 return nil , nil , fmt .Errorf ("error deriving tap hash " +
@@ -214,57 +332,3 @@ func MaybeEncodeTapscriptPreimage(t *TapscriptPreimage) ([]byte,
214332
215333 return b .Bytes (), tapHash , nil
216334}
217-
218- // NewTapBranchHash takes the raw tap hashes of the left and right nodes and
219- // hashes them into a branch.
220- func NewTapBranchHash (l , r chainhash.Hash ) chainhash.Hash {
221- if bytes .Compare (l [:], r [:]) > 0 {
222- l , r = r , l
223- }
224-
225- return * chainhash .TaggedHash (chainhash .TagTapBranch , l [:], r [:])
226- }
227-
228- // TapBranchHash computes the TapHash of a TapBranch node from its preimage
229- // if possible, otherwise an error is returned.
230- func TapBranchHash (preimage []byte ) (* chainhash.Hash , error ) {
231- // Empty preimage or leaf preimage, return typed error.
232- if len (preimage ) != tapBranchPreimageLen {
233- return nil , ErrInvalidTapscriptPreimageLen
234- }
235-
236- left := (* chainhash .Hash )(preimage [:chainhash .HashSize ])
237- right := (* chainhash .Hash )(preimage [chainhash .HashSize :])
238- h := NewTapBranchHash (* left , * right )
239-
240- return & h , nil
241- }
242-
243- // TapLeafHash computes the TapHash of a TapLeaf node from its preimage
244- // if possible, otherwise an error is returned.
245- func TapLeafHash (preimage []byte ) (* chainhash.Hash , error ) {
246- // Empty preimage.
247- if len (preimage ) == 0 {
248- return nil , ErrInvalidEmptyTapscriptPreimage
249- }
250-
251- // Enforce that it is not including another Taproot Asset commitment.
252- if bytes .Contains (preimage , TaprootAssetsMarker [:]) {
253- return nil , ErrInvalidTaprootProof
254- }
255-
256- version := txscript .TapscriptLeafVersion (preimage [0 ])
257-
258- // The script is encoded with a leading VarByte that indicates its total
259- // length.
260- remaining := preimage [1 :]
261- script , err := wire .ReadVarBytes (
262- bytes .NewReader (remaining ), 0 , uint32 (len (remaining )), "script" ,
263- )
264- if err != nil {
265- return nil , fmt .Errorf ("error decoding leaf pre-image: %w" , err )
266- }
267-
268- h := txscript .NewTapLeaf (version , script ).TapHash ()
269- return & h , nil
270- }
0 commit comments