77 "time"
88
99 "github.com/btcsuite/btcd/btcec/v2/schnorr"
10+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1011 "github.com/golang-migrate/migrate/v4"
1112 "github.com/golang-migrate/migrate/v4/database"
1213 "github.com/lightninglabs/taproot-assets/asset"
@@ -20,6 +21,11 @@ const (
2021 // Migration33ScriptKeyType is the version of the migration that
2122 // introduces the script key type.
2223 Migration33ScriptKeyType = 33
24+
25+ // Migration37InsertAssetBurns is the version of the migration that
26+ // inserts the asset burns into the specific asset burns table by
27+ // querying all assets and detecting burns from their witnesses.
28+ Migration37InsertAssetBurns = 37
2329)
2430
2531// postMigrationCheck is a function type for a function that performs a
3238 // applied. These functions are used to perform additional checks on the
3339 // database state that are not fully expressible in SQL.
3440 postMigrationChecks = map [uint ]postMigrationCheck {
35- Migration33ScriptKeyType : determineAndAssignScriptKeyType ,
41+ Migration33ScriptKeyType : determineAndAssignScriptKeyType ,
42+ Migration37InsertAssetBurns : insertAssetBurns ,
3643 }
3744)
3845
@@ -216,3 +223,97 @@ func determineAndAssignScriptKeyType(ctx context.Context,
216223
217224 return nil
218225}
226+
227+ // insertAssetBurns queries all assets and detects burns from their witnesses,
228+ // then inserts the asset burns into the specific asset burns table.
229+ func insertAssetBurns (ctx context.Context , q sqlc.Querier ) error {
230+ defaultClock := clock .NewDefaultClock ()
231+
232+ log .Debugf ("Detecting script key types" )
233+
234+ // We start by fetching all assets, even the spent ones. We then collect
235+ // a list of the burn keys from the assets (because burn keys can only
236+ // be calculated from the asset's witness).
237+ assetFilter := QueryAssetFilters {
238+ Now : sql.NullTime {
239+ Time : defaultClock .Now ().UTC (),
240+ Valid : true ,
241+ },
242+ }
243+ dbAssets , assetWitnesses , err := fetchAssetsWithWitness (
244+ ctx , q , assetFilter ,
245+ )
246+ if err != nil {
247+ return fmt .Errorf ("error fetching assets: %w" , err )
248+ }
249+
250+ chainAssets , err := dbAssetsToChainAssets (
251+ dbAssets , assetWitnesses , defaultClock ,
252+ )
253+ if err != nil {
254+ return fmt .Errorf ("error converting assets: %w" , err )
255+ }
256+
257+ burnAssets := fn .Filter (chainAssets , func (a * asset.ChainAsset ) bool {
258+ return a .IsBurn ()
259+ })
260+
261+ burnsInTable , err := q .QueryBurns (ctx , sqlc.QueryBurnsParams {})
262+ if err != nil {
263+ return err
264+ }
265+
266+ burnsMatch := func (b sqlc.QueryBurnsRow , a * asset.ChainAsset ) bool {
267+ txMatch := (chainhash .Hash (b .AnchorTxid )) == a .AnchorTx .TxHash ()
268+ assetIDMatch := (asset .ID (b .AssetID )) == a .ID ()
269+ amountMatch := uint64 (b .Amount ) == a .Amount
270+ return txMatch && assetIDMatch && amountMatch
271+ }
272+ burnAssetsNotInTable := fn .Filter (
273+ burnAssets , func (a * asset.ChainAsset ) bool {
274+ return fn .NotAny (
275+ burnsInTable , func (b sqlc.QueryBurnsRow ) bool {
276+ return burnsMatch (b , a )
277+ },
278+ )
279+ },
280+ )
281+
282+ log .Debugf ("Found %d asset burns not in burn table, adding them now" ,
283+ len (burnAssetsNotInTable ))
284+ for _ , burnAsset := range burnAssetsNotInTable {
285+ assetTransfers , err := q .QueryAssetTransfers (ctx , TransferQuery {
286+ AnchorTxHash : fn .ByteSlice (burnAsset .AnchorTx .TxHash ()),
287+ })
288+ if err != nil {
289+ return fmt .Errorf ("unable to query asset transfers: %w" ,
290+ err )
291+ }
292+ if len (assetTransfers ) != 1 {
293+ log .Warnf ("Found %d asset transfers for burn asset " +
294+ "%s, expected 1: %v" , len (assetTransfers ),
295+ burnAsset .ID (), assetTransfers )
296+
297+ continue
298+ }
299+ assetTransfer := assetTransfers [0 ]
300+
301+ var groupKeyBytes []byte
302+ if burnAsset .GroupKey != nil {
303+ gk := burnAsset .GroupKey .GroupPubKey
304+ groupKeyBytes = gk .SerializeCompressed ()
305+ }
306+
307+ _ , err = q .InsertBurn (ctx , sqlc.InsertBurnParams {
308+ TransferID : assetTransfer .ID ,
309+ AssetID : fn .ByteSlice (burnAsset .ID ()),
310+ GroupKey : groupKeyBytes ,
311+ Amount : int64 (burnAsset .Amount ),
312+ })
313+ if err != nil {
314+ return fmt .Errorf ("error inserting burn: %w" , err )
315+ }
316+ }
317+
318+ return nil
319+ }
0 commit comments