@@ -21,6 +21,7 @@ import (
2121 "github.com/btcsuite/btcd/txscript"
2222 "github.com/btcsuite/btcd/wire"
2323 "github.com/davecgh/go-spew/spew"
24+ "github.com/lightninglabs/lndclient"
2425 tap "github.com/lightninglabs/taproot-assets"
2526 "github.com/lightninglabs/taproot-assets/asset"
2627 "github.com/lightninglabs/taproot-assets/commitment"
@@ -2058,6 +2059,259 @@ func TestBatchedAssetIssuance(t *testing.T) {
20582059 }
20592060}
20602061
2062+ // TestGroupKeyRevealV1WitnessWithCustomRoot tests the different possible spend
2063+ // paths for a group key reveal witness if there are custom scripts.
2064+ func TestGroupKeyRevealV1WitnessWithCustomRoot (t * testing.T ) {
2065+ var (
2066+ ctx = context .Background ()
2067+ mockKeyRing = tapgarden .NewMockKeyRing ()
2068+ mockSigner = tapgarden .NewMockGenSigner (mockKeyRing )
2069+ txBuilder = & tapscript.GroupTxBuilder {}
2070+ txValidator = & tap.ValidatorV0 {}
2071+ hashLockPreimage = []byte ("foobar" )
2072+ )
2073+
2074+ // We expect two keys to be derived from the mock.
2075+ go func () {
2076+ <- mockKeyRing .ReqKeys
2077+ <- mockKeyRing .ReqKeys
2078+ }()
2079+
2080+ // The internal key is for the actual internal key of the group.
2081+ internalKeyDesc , err := mockKeyRing .DeriveNextTaprootAssetKey (ctx )
2082+ require .NoError (t , err )
2083+
2084+ // The second key is used for a signature spend within a tapscript leaf
2085+ // of the custom tapscript tree.
2086+ secondKeyDesc , err := mockKeyRing .DeriveNextTaprootAssetKey (ctx )
2087+ require .NoError (t , err )
2088+
2089+ hashLockLeaf := test .ScriptHashLock (t , hashLockPreimage )
2090+ schnorrSigLeaf := test .ScriptSchnorrSig (t , secondKeyDesc .PubKey )
2091+
2092+ userRoot := txscript .AssembleTaprootScriptTree (
2093+ hashLockLeaf , schnorrSigLeaf ,
2094+ ).RootNode .TapHash ()
2095+
2096+ spendTestCases := []struct {
2097+ name string
2098+ genWitness func (* testing.T , * asset.Asset ,
2099+ asset.GroupKeyRevealV1 ) wire.TxWitness
2100+ }{{
2101+ name : "key spend" ,
2102+ genWitness : func (t * testing.T , a * asset.Asset ,
2103+ gkr asset.GroupKeyRevealV1 ) wire.TxWitness {
2104+
2105+ genTx , prevOut , err := txBuilder .BuildGenesisTx (a )
2106+ require .NoError (t , err )
2107+
2108+ witness , err := signGroupKeyV1 (
2109+ internalKeyDesc , gkr , genTx , prevOut ,
2110+ mockSigner , nil ,
2111+ )
2112+ require .NoError (t , err )
2113+
2114+ return witness
2115+ },
2116+ }, {
2117+ name : "script spend with preimage" ,
2118+ genWitness : func (t * testing.T , a * asset.Asset ,
2119+ gkr asset.GroupKeyRevealV1 ) wire.TxWitness {
2120+
2121+ controlBlock , err := gkr .ScriptSpendControlBlock (
2122+ a .ID (),
2123+ )
2124+ require .NoError (t , err )
2125+
2126+ controlBlock .InclusionProof = bytes .Join ([][]byte {
2127+ fn .ByteSlice (schnorrSigLeaf .TapHash ()),
2128+ controlBlock .InclusionProof ,
2129+ }, nil )
2130+ controlBlockBytes , err := controlBlock .ToBytes ()
2131+ require .NoError (t , err )
2132+
2133+ // Witness is just the preimage, the script and the
2134+ // control block.
2135+ return wire.TxWitness {
2136+ hashLockPreimage ,
2137+ hashLockLeaf .Script ,
2138+ controlBlockBytes ,
2139+ }
2140+ },
2141+ }, {
2142+ name : "script spend with signature" ,
2143+ genWitness : func (t * testing.T , a * asset.Asset ,
2144+ gkr asset.GroupKeyRevealV1 ) wire.TxWitness {
2145+
2146+ genTx , prevOut , err := txBuilder .BuildGenesisTx (a )
2147+ require .NoError (t , err )
2148+
2149+ controlBlock , err := gkr .ScriptSpendControlBlock (
2150+ a .ID (),
2151+ )
2152+ require .NoError (t , err )
2153+
2154+ controlBlock .InclusionProof = bytes .Join ([][]byte {
2155+ fn .ByteSlice (hashLockLeaf .TapHash ()),
2156+ controlBlock .InclusionProof ,
2157+ }, nil )
2158+ controlBlockBytes , err := controlBlock .ToBytes ()
2159+ require .NoError (t , err )
2160+
2161+ leafToSign := & psbt.TaprootTapLeafScript {
2162+ ControlBlock : controlBlockBytes ,
2163+ Script : schnorrSigLeaf .Script ,
2164+ LeafVersion : txscript .BaseLeafVersion ,
2165+ }
2166+
2167+ witness , err := signGroupKeyV1 (
2168+ secondKeyDesc , gkr , genTx , prevOut , mockSigner ,
2169+ leafToSign ,
2170+ )
2171+ require .NoError (t , err )
2172+
2173+ return witness
2174+ },
2175+ }}
2176+
2177+ for _ , tc := range spendTestCases {
2178+ t .Run (tc .name , func (tt * testing.T ) {
2179+ randAsset := asset .RandAsset (tt , asset .Normal )
2180+ genAssetID := randAsset .ID ()
2181+ groupKeyReveal , err := asset .NewGroupKeyRevealV1 (
2182+ * internalKeyDesc .PubKey , genAssetID ,
2183+ fn .Some (userRoot ),
2184+ )
2185+ require .NoError (tt , err )
2186+
2187+ // Set the group key on the asset, since it's a randomly
2188+ // created group key otherwise.
2189+ groupPubKey , err := groupKeyReveal .GroupPubKey (
2190+ genAssetID ,
2191+ )
2192+ require .NoError (tt , err )
2193+ randAsset .GroupKey = & asset.GroupKey {
2194+ RawKey : internalKeyDesc ,
2195+ GroupPubKey : * groupPubKey ,
2196+ TapscriptRoot : groupKeyReveal .TapscriptRoot (),
2197+ }
2198+ randAsset .PrevWitnesses = []asset.Witness {
2199+ {
2200+ PrevID : & asset.PrevID {},
2201+ },
2202+ }
2203+
2204+ witness := tc .genWitness (tt , randAsset , groupKeyReveal )
2205+ randAsset .PrevWitnesses [0 ].TxWitness = witness
2206+
2207+ err = txValidator .Execute (
2208+ randAsset , nil , nil , proof .MockChainLookup ,
2209+ )
2210+ require .NoError (tt , err )
2211+ })
2212+ }
2213+ }
2214+
2215+ // TestGroupKeyRevealV1WitnessNoScripts tests the key spend path for a group key
2216+ // reveal witness if there are no custom scripts.
2217+ func TestGroupKeyRevealV1WitnessNoScripts (t * testing.T ) {
2218+ var (
2219+ ctx = context .Background ()
2220+ mockKeyRing = tapgarden .NewMockKeyRing ()
2221+ mockSigner = tapgarden .NewMockGenSigner (mockKeyRing )
2222+ txBuilder = & tapscript.GroupTxBuilder {}
2223+ txValidator = & tap.ValidatorV0 {}
2224+ )
2225+
2226+ // We expect just one key to be derived from the mock.
2227+ go func () {
2228+ <- mockKeyRing .ReqKeys
2229+ }()
2230+
2231+ // The internal key is for the actual internal key of the group.
2232+ internalKeyDesc , err := mockKeyRing .DeriveNextTaprootAssetKey (ctx )
2233+ require .NoError (t , err )
2234+
2235+ randAsset := asset .RandAsset (t , asset .Normal )
2236+ genAssetID := randAsset .ID ()
2237+ groupKeyReveal , err := asset .NewGroupKeyRevealV1 (
2238+ * internalKeyDesc .PubKey , genAssetID , fn .None [chainhash.Hash ](),
2239+ )
2240+ require .NoError (t , err )
2241+
2242+ // Set the group key on the asset, since it's a randomly created group
2243+ // key otherwise.
2244+ groupPubKey , err := groupKeyReveal .GroupPubKey (genAssetID )
2245+ require .NoError (t , err )
2246+ randAsset .GroupKey = & asset.GroupKey {
2247+ RawKey : internalKeyDesc ,
2248+ GroupPubKey : * groupPubKey ,
2249+ TapscriptRoot : groupKeyReveal .TapscriptRoot (),
2250+ }
2251+ randAsset .PrevWitnesses = []asset.Witness {
2252+ {
2253+ PrevID : & asset.PrevID {},
2254+ },
2255+ }
2256+
2257+ genTx , prevOut , err := txBuilder .BuildGenesisTx (randAsset )
2258+ require .NoError (t , err )
2259+
2260+ witness , err := signGroupKeyV1 (
2261+ internalKeyDesc , groupKeyReveal , genTx , prevOut , mockSigner ,
2262+ nil ,
2263+ )
2264+ require .NoError (t , err )
2265+
2266+ randAsset .PrevWitnesses [0 ].TxWitness = witness
2267+
2268+ err = txValidator .Execute (
2269+ randAsset , nil , nil , proof .MockChainLookup ,
2270+ )
2271+ require .NoError (t , err )
2272+ }
2273+
2274+ // signGroupKeyV1 is the equivalent for asset.DeriveGroupKey but for a V1 key.
2275+ func signGroupKeyV1 (keyDesc keychain.KeyDescriptor , gk asset.GroupKeyRevealV1 ,
2276+ genTx * wire.MsgTx , prevOut * wire.TxOut , signer asset.GenesisSigner ,
2277+ leafToSign * psbt.TaprootTapLeafScript ) (wire.TxWitness , error ) {
2278+
2279+ signDesc := & lndclient.SignDescriptor {
2280+ KeyDesc : keyDesc ,
2281+ TapTweak : gk .TapscriptRoot (),
2282+ Output : prevOut ,
2283+ HashType : txscript .SigHashDefault ,
2284+ InputIndex : 0 ,
2285+ SignMethod : input .TaprootKeySpendSignMethod ,
2286+ }
2287+
2288+ if leafToSign != nil {
2289+ signDesc .SignMethod = input .TaprootScriptSpendSignMethod
2290+ signDesc .WitnessScript = leafToSign .Script
2291+ }
2292+
2293+ sig , err := signer .SignVirtualTx (signDesc , genTx , prevOut )
2294+ if err != nil {
2295+ return nil , err
2296+ }
2297+
2298+ witness := wire.TxWitness {sig .Serialize ()}
2299+
2300+ // If this was a script spend, we also have to add the script itself and
2301+ // the control block to the witness, otherwise the verifier will reject
2302+ // the generated witness.
2303+ if signDesc .SignMethod == input .TaprootScriptSpendSignMethod &&
2304+ leafToSign != nil {
2305+
2306+ witness = append (
2307+ witness , signDesc .WitnessScript ,
2308+ leafToSign .ControlBlock ,
2309+ )
2310+ }
2311+
2312+ return witness , nil
2313+ }
2314+
20612315func init () {
20622316 rand .Seed (time .Now ().Unix ())
20632317
0 commit comments