@@ -16,6 +16,7 @@ import (
16
16
"github.com/lightninglabs/taproot-assets/commitment"
17
17
"github.com/lightninglabs/taproot-assets/fn"
18
18
"github.com/lightninglabs/taproot-assets/proof"
19
+ lfn "github.com/lightningnetwork/lnd/fn/v2"
19
20
"github.com/lightningnetwork/lnd/keychain"
20
21
)
21
22
@@ -105,6 +106,12 @@ type Storage interface {
105
106
// (genesis + group key) associated with a given asset.
106
107
QueryAssetGroup (context.Context , asset.ID ) (* asset.AssetGroup , error )
107
108
109
+ // QueryAssetGroupByGroupKey fetches the asset group with a matching
110
+ // tweaked key, including the genesis information used to create the
111
+ // group.
112
+ QueryAssetGroupByGroupKey (context.Context ,
113
+ * btcec.PublicKey ) (* asset.AssetGroup , error )
114
+
108
115
// FetchAssetMetaByHash attempts to fetch an asset meta based on an
109
116
// asset hash.
110
117
FetchAssetMetaByHash (ctx context.Context ,
@@ -210,38 +217,68 @@ func NewBook(cfg BookConfig) *Book {
210
217
// geneses already known to this node. If asset issuance was not previously
211
218
// verified, we then query universes in our federation for issuance proofs.
212
219
func (b * Book ) QueryAssetInfo (ctx context.Context ,
213
- id asset.ID ) (* asset.AssetGroup , error ) {
220
+ specifier asset.Specifier ) (asset.AssetGroup , error ) {
221
+
222
+ switch {
223
+ case specifier .HasGroupPubKey ():
224
+ return fn .MapOptionZ (
225
+ specifier .GroupKey (),
226
+ func (gk btcec.PublicKey ) lfn.Result [asset.AssetGroup ] {
227
+ return b .queryAssetInfoByGroupKey (ctx , gk )
228
+ },
229
+ ).Unpack ()
230
+
231
+ case specifier .HasId ():
232
+ return fn .MapOptionZ (
233
+ specifier .ID (),
234
+ func (id asset.ID ) lfn.Result [asset.AssetGroup ] {
235
+ return b .queryAssetInfoByID (ctx , id )
236
+ },
237
+ ).Unpack ()
238
+
239
+ default :
240
+ return asset.AssetGroup {}, fmt .Errorf ("invalid specifier: %s" ,
241
+ & specifier )
242
+ }
243
+ }
244
+
245
+ // queryAssetInfoByID attempts to locate asset genesis information by querying
246
+ // geneses by ID already known to this node. If asset issuance was not
247
+ // previously verified, we then query universes in our federation for issuance
248
+ // proofs.
249
+ func (b * Book ) queryAssetInfoByID (ctx context.Context ,
250
+ id asset.ID ) lfn.Result [asset.AssetGroup ] {
214
251
215
252
// Check if we know of this asset ID already.
216
253
assetGroup , err := b .cfg .Store .QueryAssetGroup (ctx , id )
217
254
switch {
218
255
case assetGroup != nil :
219
- return assetGroup , nil
256
+ return lfn . Ok ( * assetGroup )
220
257
221
258
// Asset lookup failed gracefully; continue to asset lookup using the
222
259
// AssetSyncer if enabled.
223
260
case errors .Is (err , ErrAssetGroupUnknown ):
224
261
if b .cfg .Syncer == nil {
225
- return nil , ErrAssetGroupUnknown
262
+ return lfn. Err [asset. AssetGroup ]( ErrAssetGroupUnknown )
226
263
}
227
264
228
265
case err != nil :
229
- return nil , err
266
+ return lfn. Err [asset. AssetGroup ]( err )
230
267
}
231
268
232
269
log .Debugf ("Asset %v is unknown, attempting to bootstrap" , id .String ())
233
270
234
271
// Use the AssetSyncer to query our universe federation for the asset.
235
272
err = b .cfg .Syncer .SyncAssetInfo (ctx , asset .NewSpecifierFromId (id ))
236
273
if err != nil {
237
- return nil , err
274
+ return lfn. Err [asset. AssetGroup ]( err )
238
275
}
239
276
240
277
// The asset genesis info may have been synced from a universe
241
278
// server; query for the asset ID again.
242
279
assetGroup , err = b .cfg .Store .QueryAssetGroup (ctx , id )
243
280
if err != nil {
244
- return nil , err
281
+ return lfn. Err [asset. AssetGroup ]( err )
245
282
}
246
283
247
284
log .Debugf ("Bootstrap succeeded for asset %v" , id .String ())
@@ -257,11 +294,57 @@ func (b *Book) QueryAssetInfo(ctx context.Context,
257
294
))
258
295
err = b .cfg .Syncer .EnableAssetSync (ctx , assetGroup )
259
296
if err != nil {
260
- return nil , err
297
+ return lfn.Err [asset.AssetGroup ](err )
298
+ }
299
+ }
300
+
301
+ return lfn .Ok (* assetGroup )
302
+ }
303
+
304
+ // queryAssetInfoByID attempts to locate asset genesis information by querying
305
+ // geneses by group key already known to this node. If asset issuance was not
306
+ // previously verified, we then query universes in our federation for issuance
307
+ // proofs.
308
+ func (b * Book ) queryAssetInfoByGroupKey (ctx context.Context ,
309
+ gk btcec.PublicKey ) lfn.Result [asset.AssetGroup ] {
310
+
311
+ // Check if we know of this group key already.
312
+ gkBytes := gk .SerializeCompressed ()
313
+ assetGroup , err := b .cfg .Store .QueryAssetGroupByGroupKey (ctx , & gk )
314
+ switch {
315
+ case assetGroup != nil :
316
+ return lfn .Ok (* assetGroup )
317
+
318
+ // Asset lookup failed gracefully; continue to asset lookup using the
319
+ // AssetSyncer if enabled.
320
+ case errors .Is (err , ErrAssetGroupUnknown ):
321
+ if b .cfg .Syncer == nil {
322
+ return lfn.Err [asset.AssetGroup ](ErrAssetGroupUnknown )
261
323
}
324
+
325
+ case err != nil :
326
+ return lfn.Err [asset.AssetGroup ](err )
262
327
}
263
328
264
- return assetGroup , nil
329
+ log .Debugf ("Asset group %x is unknown, attempting to bootstrap" ,
330
+ gkBytes )
331
+
332
+ // Use the AssetSyncer to query our universe federation for the asset.
333
+ err = b .SyncAssetGroup (ctx , gk )
334
+ if err != nil {
335
+ return lfn.Err [asset.AssetGroup ](err )
336
+ }
337
+
338
+ // The asset genesis info may have been synced from a universe
339
+ // server; query for the group key again.
340
+ assetGroup , err = b .cfg .Store .QueryAssetGroupByGroupKey (ctx , & gk )
341
+ if err != nil {
342
+ return lfn.Err [asset.AssetGroup ](err )
343
+ }
344
+
345
+ log .Debugf ("Bootstrap succeeded for group %x" , gkBytes )
346
+
347
+ return lfn .Ok (* assetGroup )
265
348
}
266
349
267
350
// SyncAssetGroup attempts to enable asset sync for the given asset group, then
@@ -336,16 +419,16 @@ func (b *Book) FetchAssetMetaForAsset(ctx context.Context,
336
419
337
420
// NewAddress creates a new Taproot Asset address based on the input parameters.
338
421
func (b * Book ) NewAddress (ctx context.Context , addrVersion Version ,
339
- assetID asset.ID , amount uint64 ,
422
+ specifier asset.Specifier , amount uint64 ,
340
423
tapscriptSibling * commitment.TapscriptPreimage ,
341
424
proofCourierAddr url.URL , addrOpts ... NewAddrOpt ) (* AddrWithKeyInfo ,
342
425
error ) {
343
426
344
427
// Before we proceed and make new keys, make sure that we actually know
345
428
// of this asset ID, or can import it.
346
- if _ , err := b .QueryAssetInfo (ctx , assetID ); err != nil {
429
+ if _ , err := b .QueryAssetInfo (ctx , specifier ); err != nil {
347
430
return nil , fmt .Errorf ("unable to make address for unknown " +
348
- "asset %x : %w" , assetID [:] , err )
431
+ "asset %s : %w" , & specifier , err )
349
432
}
350
433
351
434
rawScriptKeyDesc , err := b .cfg .KeyRing .DeriveNextTaprootAssetKey (ctx )
@@ -364,15 +447,15 @@ func (b *Book) NewAddress(ctx context.Context, addrVersion Version,
364
447
}
365
448
366
449
return b .NewAddressWithKeys (
367
- ctx , addrVersion , assetID , amount , scriptKey , internalKeyDesc ,
450
+ ctx , addrVersion , specifier , amount , scriptKey , internalKeyDesc ,
368
451
tapscriptSibling , proofCourierAddr , addrOpts ... ,
369
452
)
370
453
}
371
454
372
455
// NewAddressWithKeys creates a new Taproot Asset address based on the input
373
456
// parameters that include pre-derived script and internal keys.
374
457
func (b * Book ) NewAddressWithKeys (ctx context.Context , addrVersion Version ,
375
- assetID asset.ID , amount uint64 , scriptKey asset.ScriptKey ,
458
+ specifier asset.Specifier , amount uint64 , scriptKey asset.ScriptKey ,
376
459
internalKeyDesc keychain.KeyDescriptor ,
377
460
tapscriptSibling * commitment.TapscriptPreimage ,
378
461
proofCourierAddr url.URL , addrOpts ... NewAddrOpt ) (* AddrWithKeyInfo ,
@@ -381,7 +464,7 @@ func (b *Book) NewAddressWithKeys(ctx context.Context, addrVersion Version,
381
464
// Before we proceed, we'll make sure that the asset group is known to
382
465
// the local store. Otherwise, we can't make an address as we haven't
383
466
// bootstrapped it.
384
- assetGroup , err := b .QueryAssetInfo (ctx , assetID )
467
+ assetGroup , err := b .QueryAssetInfo (ctx , specifier )
385
468
if err != nil {
386
469
return nil , err
387
470
}
0 commit comments