From 98fa90d868abcc537ac68c68bbe26edce436adc8 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Tue, 26 Nov 2024 20:37:31 +0100 Subject: [PATCH 1/6] tapfreighter: add AssetBurn to tapfreighter --- tapfreighter/interface.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tapfreighter/interface.go b/tapfreighter/interface.go index 59fb6016a..3105b62a6 100644 --- a/tapfreighter/interface.go +++ b/tapfreighter/interface.go @@ -39,6 +39,24 @@ type CommitmentConstraints struct { CoinSelectType tapsend.CoinSelectType } +// AssetBurn holds data related to a burn of an asset. +type AssetBurn struct { + // Note is a user provided description for the transfer. + Note string + + // AssetID is the ID of the burnt asset. + AssetID []byte + + // GroupKey is the group key of the group the burnt asset belongs to. + GroupKey []byte + + // Amount is the amount of the asset that got burnt. + Amount uint64 + + // AnchorTxid is the txid of the transaction this burn is anchored to. + AnchorTxid chainhash.Hash +} + // String returns the string representation of the commitment constraints. func (c *CommitmentConstraints) String() string { assetIDBytes, groupKeyBytes := c.AssetSpecifier.AsBytes() From a2d1de120f4df1799ef044f90a63aa91c7d68ce7 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 7 Nov 2024 19:15:25 +0200 Subject: [PATCH 2/6] tapdb: add asset burns table and queries --- tapdb/assets_store.go | 78 +++++- tapdb/assets_store_test.go | 276 +++++++++++++++++++- tapdb/migrations.go | 2 +- tapdb/sqlc/migrations/000025_burns.down.sql | 1 + tapdb/sqlc/migrations/000025_burns.up.sql | 19 ++ tapdb/sqlc/models.go | 9 + tapdb/sqlc/querier.go | 2 + tapdb/sqlc/queries/transfers.sql | 30 +++ tapdb/sqlc/transfers.sql.go | 96 +++++++ tapfreighter/chain_porter.go | 2 +- tapfreighter/interface.go | 3 +- 11 files changed, 513 insertions(+), 5 deletions(-) create mode 100644 tapdb/sqlc/migrations/000025_burns.down.sql create mode 100644 tapdb/sqlc/migrations/000025_burns.up.sql diff --git a/tapdb/assets_store.go b/tapdb/assets_store.go index 1cd1314a9..d38da9d7e 100644 --- a/tapdb/assets_store.go +++ b/tapdb/assets_store.go @@ -179,6 +179,10 @@ type ( // when fetching a tapscript tree, which includes the serialized node // and the node index in the tree. TapscriptTreeNode = sqlc.FetchTapscriptTreeRow + + // QueryBurnsFilters is a set of filters that is applied on the set of + // the returned burns. + QueryBurnsFilters = sqlc.QueryBurnsParams ) // ActiveAssetsStore is a sub-set of the main sqlc.Querier interface that @@ -358,6 +362,15 @@ type ActiveAssetsStore interface { // FetchAssetMetaForAsset fetches the asset meta for a given asset. FetchAssetMetaForAsset(ctx context.Context, assetID []byte) (sqlc.FetchAssetMetaForAssetRow, error) + + // InsertBurn inserts a new row to the asset burns table which + // includes all important data related to the burn. + InsertBurn(ctx context.Context, arg sqlc.InsertBurnParams) (int64, + error) + + // QueryBurns returns all burn entries that match the passed filters. + QueryBurns(ctx context.Context, + arg sqlc.QueryBurnsParams) ([]sqlc.QueryBurnsRow, error) } // MetaStore is a sub-set of the main sqlc.Querier interface that contains @@ -2951,7 +2964,8 @@ func (a *AssetStore) ConfirmProofDelivery(ctx context.Context, // confirmation of the anchor transaction, ensuring the on-chain reference // information is up to date. func (a *AssetStore) LogAnchorTxConfirm(ctx context.Context, - conf *tapfreighter.AssetConfirmEvent) error { + conf *tapfreighter.AssetConfirmEvent, + burns []*tapfreighter.AssetBurn) error { var ( writeTxOpts AssetStoreTxOptions @@ -3154,6 +3168,25 @@ func (a *AssetStore) LogAnchorTxConfirm(ctx context.Context, // longer an unspent output, however we'll keep it in order to // be able to reconstruct transfer history. + // We now insert in the DB any burns that may have been present + // in the transfer. + for _, b := range burns { + _, err = q.InsertBurn(ctx, sqlc.InsertBurnParams{ + TransferID: int32(assetTransfer.ID), + Note: sql.NullString{ + String: b.Note, + Valid: b.Note != "", + }, + AssetID: b.AssetID, + GroupKey: b.GroupKey, + Amount: int64(b.Amount), + }) + if err != nil { + return fmt.Errorf("failed to insert burn in "+ + "db: %v", err) + } + } + return nil }) if err != nil { @@ -3528,6 +3561,49 @@ func (a *AssetStore) TxHeight(ctx context.Context, txid chainhash.Hash) (uint32, return uint32(dbBlockHeight), nil } +// QueryBurns queries burnt assets based on the passed filters. +func (a *AssetStore) QueryBurns(ctx context.Context, + filters QueryBurnsFilters) ([]*tapfreighter.AssetBurn, error) { + + var res []*tapfreighter.AssetBurn + + readOpts := NewAssetStoreReadTx() + dbErr := a.db.ExecTx(ctx, &readOpts, func(q ActiveAssetsStore) error { + burns, err := q.QueryBurns(ctx, sqlc.QueryBurnsParams{ + AssetID: filters.AssetID, + GroupKey: filters.GroupKey, + AnchorTxid: filters.AnchorTxid, + }) + if err != nil { + return err + } + + for _, b := range burns { + burn := marshalAssetBurnTransfer(b) + + res = append(res, burn) + } + + return nil + }) + if dbErr != nil { + return nil, dbErr + } + + return res, nil +} + +// marshalAssetBurnTransfer converts the db row of a burn to a tapdb.AssetBurn. +func marshalAssetBurnTransfer(row sqlc.QueryBurnsRow) *tapfreighter.AssetBurn { + return &tapfreighter.AssetBurn{ + Note: row.Note.String, + AssetID: row.AssetID, + GroupKey: row.GroupKey, + Amount: uint64(row.Amount), + AnchorTxid: chainhash.Hash(row.AnchorTxid), + } +} + // A compile-time constraint to ensure that AssetStore meets the // proof.NotifyArchiver interface. var _ proof.NotifyArchiver = (*AssetStore)(nil) diff --git a/tapdb/assets_store_test.go b/tapdb/assets_store_test.go index b619454b7..5b177821d 100644 --- a/tapdb/assets_store_test.go +++ b/tapdb/assets_store_test.go @@ -21,6 +21,7 @@ import ( "github.com/lightninglabs/taproot-assets/internal/test" "github.com/lightninglabs/taproot-assets/mssmt" "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapdb/sqlc" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tapscript" "github.com/lightninglabs/taproot-assets/tapsend" @@ -1532,7 +1533,7 @@ func TestAssetExportLog(t *testing.T) { BlockHeight: blockHeight, BlockHash: fakeBlockHash, FinalProofs: proofs, - }, + }, nil, ) require.NoError(t, err) @@ -2206,6 +2207,279 @@ func TestTransferOutputProofDeliveryStatus(t *testing.T) { ) } +func TestQueryAssetBurns(t *testing.T) { + t.Parallel() + + // First, we'll create a new assets store. We'll use this to store the + // asset and the outbound parcel in the database. + _, assetsStore, db := newAssetStore(t) + ctx := context.Background() + + // Generate a single asset. + targetScriptKey := asset.NewScriptKeyBip86(keychain.KeyDescriptor{ + PubKey: test.RandPubKey(t), + KeyLocator: keychain.KeyLocator{ + Family: test.RandInt[keychain.KeyFamily](), + Index: uint32(test.RandInt[int32]()), + }, + }) + + assetVersionV0 := asset.V0 + + const numAssets = 1 + assetGen := newAssetGenerator(t, numAssets, 1) + assetGen.genAssets(t, assetsStore, []assetDesc{ + { + assetGen: assetGen.assetGens[0], + anchorPoint: assetGen.anchorPoints[0], + + // This is the script key of the asset we'll be + // modifying. + scriptKey: &targetScriptKey, + + amt: 16, + assetVersion: &assetVersionV0, + }, + }) + + // Formulate a spend delta outbound parcel. This parcel will be stored + // in the database. We will then manipulate the proof delivery status + // of the first transfer output. + // + // First, we'll generate a new anchor transaction for use in the parcel. + newAnchorTx := wire.NewMsgTx(2) + newAnchorTx.AddTxIn(&wire.TxIn{}) + newAnchorTx.TxIn[0].SignatureScript = []byte{} + newAnchorTx.AddTxOut(&wire.TxOut{ + PkScript: bytes.Repeat([]byte{0x01}, 34), + Value: 1000, + }) + anchorTxHash := newAnchorTx.TxHash() + + // Next, we'll generate script keys for the two transfer outputs. + newScriptKey := asset.NewScriptKeyBip86(keychain.KeyDescriptor{ + PubKey: test.RandPubKey(t), + KeyLocator: keychain.KeyLocator{ + Index: uint32(rand.Int31()), + Family: keychain.KeyFamily(rand.Int31()), + }, + }) + + newScriptKey2 := asset.NewScriptKeyBip86(keychain.KeyDescriptor{ + PubKey: test.RandPubKey(t), + KeyLocator: keychain.KeyLocator{ + Index: uint32(rand.Int31()), + Family: keychain.KeyFamily(rand.Int31()), + }, + }) + + // The outbound parcel will split the asset into two outputs. The first + // will have an amount of 9, and the second will have the remainder of + // the asset amount. + newAmt := 9 + + senderBlob := bytes.Repeat([]byte{0x01}, 100) + receiverBlob := bytes.Repeat([]byte{0x02}, 100) + + newWitness := asset.Witness{ + PrevID: &asset.PrevID{}, + TxWitness: [][]byte{{0x01}, {0x02}}, + SplitCommitment: nil, + } + + // Mock proof courier address. + proofCourierAddrBytes := []byte("universerpc://localhost:10009") + + // Fetch the asset that was previously generated. + allAssets, err := assetsStore.FetchAllAssets(ctx, true, false, nil) + require.NoError(t, err) + require.Len(t, allAssets, numAssets) + + inputAsset := allAssets[0] + + // Construct the outbound parcel that will be stored in the database. + spendDelta := &tapfreighter.OutboundParcel{ + AnchorTx: newAnchorTx, + AnchorTxHeightHint: 1450, + ChainFees: int64(100), + Inputs: []tapfreighter.TransferInput{{ + PrevID: asset.PrevID{ + OutPoint: wire.OutPoint{ + Hash: assetGen.anchorTxs[0].TxHash(), + Index: 0, + }, + ID: inputAsset.ID(), + ScriptKey: asset.ToSerialized( + inputAsset.ScriptKey.PubKey, + ), + }, + Amount: inputAsset.Amount, + }}, + Outputs: []tapfreighter.TransferOutput{{ + Anchor: tapfreighter.Anchor{ + Value: 1000, + OutPoint: wire.OutPoint{ + Hash: anchorTxHash, + Index: 0, + }, + InternalKey: keychain.KeyDescriptor{ + PubKey: test.RandPubKey(t), + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily( + rand.Int31(), + ), + Index: uint32( + test.RandInt[int32](), + ), + }, + }, + TaprootAssetRoot: bytes.Repeat([]byte{0x1}, 32), + MerkleRoot: bytes.Repeat([]byte{0x1}, 32), + }, + ScriptKey: newScriptKey, + ScriptKeyLocal: false, + Amount: uint64(newAmt), + LockTime: 1337, + RelativeLockTime: 31337, + WitnessData: []asset.Witness{newWitness}, + SplitCommitmentRoot: nil, + AssetVersion: asset.V0, + ProofSuffix: receiverBlob, + ProofCourierAddr: proofCourierAddrBytes, + ProofDeliveryComplete: fn.Some[bool](false), + Position: 0, + }, { + Anchor: tapfreighter.Anchor{ + Value: 1000, + OutPoint: wire.OutPoint{ + Hash: anchorTxHash, + Index: 1, + }, + InternalKey: keychain.KeyDescriptor{ + PubKey: test.RandPubKey(t), + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily( + rand.Int31(), + ), + Index: uint32( + test.RandInt[int32](), + ), + }, + }, + TaprootAssetRoot: bytes.Repeat([]byte{0x1}, 32), + MerkleRoot: bytes.Repeat([]byte{0x1}, 32), + }, + ScriptKey: newScriptKey2, + ScriptKeyLocal: true, + Amount: inputAsset.Amount - uint64(newAmt), + WitnessData: []asset.Witness{newWitness}, + SplitCommitmentRoot: nil, + AssetVersion: asset.V1, + ProofSuffix: senderBlob, + Position: 1, + }}, + } + + // Store the outbound parcel in the database. + leaseOwner := fn.ToArray[[32]byte](test.RandBytes(32)) + leaseExpiry := time.Now().Add(time.Hour) + require.NoError(t, assetsStore.LogPendingParcel( + ctx, spendDelta, leaseOwner, leaseExpiry, + )) + + // At this point, we should be able to query for the log parcel, by + // looking for all unconfirmed transfers. + assetTransfers, err := db.QueryAssetTransfers(ctx, TransferQuery{ + PendingTransfersOnly: sqlBool(true), + }) + require.NoError(t, err) + require.Len(t, assetTransfers, 1) + + // This transfer's anchor transaction is unconfirmed. Therefore, the + // anchor transaction block hash field of the transfer should be unset. + require.Empty(t, assetTransfers[0].AnchorTxBlockHash) + + // At this point we will confirm the anchor tx on-chain. + assetTransfer := assetTransfers[0] + randBlockHash := test.RandHash() + + err = db.ConfirmChainAnchorTx(ctx, AnchorTxConf{ + Txid: assetTransfer.Txid, + BlockHash: randBlockHash[:], + BlockHeight: sqlInt32(441), + TxIndex: sqlInt32(1), + }) + require.NoError(t, err) + + // We should also be able to find the transfer outputs. + transferOutputs, err := db.FetchTransferOutputs( + ctx, assetTransfers[0].ID, + ) + require.NoError(t, err) + require.Len(t, transferOutputs, 2) + + // We will now set the status of the transfer output proof to + // "delivered". + // + // nolint: lll + err = db.SetTransferOutputProofDeliveryStatus( + ctx, OutputProofDeliveryStatus{ + DeliveryComplete: sqlBool(true), + SerializedAnchorOutpoint: transferOutputs[0].AnchorOutpoint, + Position: transferOutputs[0].Position, + }, + ) + require.NoError(t, err) + + // Given that the asset transfer is completely finalised, we should be + // able to find it among the confirmed transfers. We will test this by + // retrieving the transfer by not specifying the pending transfers only + // flag and, in another attempt, by setting the flag to false. + assetTransfers, err = db.QueryAssetTransfers(ctx, TransferQuery{}) + require.NoError(t, err) + require.Len(t, assetTransfers, 1) + + // Let's insert a burn. + assetID := inputAsset.ID() + + _, err = assetsStore.db.InsertBurn(ctx, sqlc.InsertBurnParams{ + TransferID: int32(assetTransfers[0].ID), + Note: sql.NullString{ + String: "burn", + Valid: true, + }, + AssetID: assetID[:], + GroupKey: nil, + Amount: 424242, + }) + require.NoError(t, err) + + burns, err := assetsStore.QueryBurns(ctx, sqlc.QueryBurnsParams{}) + + // We should have one burn. + require.NoError(t, err) + require.Len(t, burns, 1) + + _, err = assetsStore.db.InsertBurn(ctx, sqlc.InsertBurnParams{ + TransferID: int32(assetTransfers[0].ID), + Note: sql.NullString{ + String: "burn", + Valid: true, + }, + AssetID: assetID[:], + GroupKey: nil, + Amount: 424242, + }) + require.NoError(t, err) + + // If we filter burns by the asset ID we should have 2 burns. + burns, err = assetsStore.QueryBurns(ctx, sqlc.QueryBurnsParams{ + AssetID: assetID[:], + }) + require.NoError(t, err) + require.Len(t, burns, 2) +} + func TestQueryAssetBalances(t *testing.T) { t.Parallel() diff --git a/tapdb/migrations.go b/tapdb/migrations.go index e49bc6e1d..e8e5a85cc 100644 --- a/tapdb/migrations.go +++ b/tapdb/migrations.go @@ -22,7 +22,7 @@ const ( // daemon. // // NOTE: This MUST be updated when a new migration is added. - LatestMigrationVersion = 24 + LatestMigrationVersion = 25 ) // MigrationTarget is a functional option that can be passed to applyMigrations diff --git a/tapdb/sqlc/migrations/000025_burns.down.sql b/tapdb/sqlc/migrations/000025_burns.down.sql new file mode 100644 index 000000000..15610aca3 --- /dev/null +++ b/tapdb/sqlc/migrations/000025_burns.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS asset_burn_transfers; diff --git a/tapdb/sqlc/migrations/000025_burns.up.sql b/tapdb/sqlc/migrations/000025_burns.up.sql new file mode 100644 index 000000000..c775761fe --- /dev/null +++ b/tapdb/sqlc/migrations/000025_burns.up.sql @@ -0,0 +1,19 @@ +CREATE TABLE IF NOT EXISTS asset_burn_transfers ( + -- The auto-incrementing integer that identifies this burn transfer. + burn_id INTEGER PRIMARY KEY, + + -- A reference to the primary key of the transfer that includes this burn. + transfer_id INTEGER NOT NULL REFERENCES asset_transfers(id), + + -- A note that may contain user defined metadata. + note TEXT, + + -- The asset id of the burnt asset. + asset_id BLOB NOT NULL REFERENCES genesis_assets(asset_id), + + -- The group key of the group the burnt asset belonged to. + group_key BLOB REFERENCES asset_groups(tweaked_group_key), + + -- The amount of the asset that was burned. + amount BIGINT NOT NULL +) \ No newline at end of file diff --git a/tapdb/sqlc/models.go b/tapdb/sqlc/models.go index 4a7aba095..14e9f8f86 100644 --- a/tapdb/sqlc/models.go +++ b/tapdb/sqlc/models.go @@ -54,6 +54,15 @@ type Asset struct { Spent bool } +type AssetBurnTransfer struct { + BurnID int64 + TransferID int32 + Note sql.NullString + AssetID []byte + GroupKey []byte + Amount int64 +} + type AssetGroup struct { GroupID int64 TweakedGroupKey []byte diff --git a/tapdb/sqlc/querier.go b/tapdb/sqlc/querier.go index ccf31d99d..e65db7ba6 100644 --- a/tapdb/sqlc/querier.go +++ b/tapdb/sqlc/querier.go @@ -101,6 +101,7 @@ type Querier interface { InsertAssetTransferInput(ctx context.Context, arg InsertAssetTransferInputParams) error InsertAssetTransferOutput(ctx context.Context, arg InsertAssetTransferOutputParams) error InsertBranch(ctx context.Context, arg InsertBranchParams) error + InsertBurn(ctx context.Context, arg InsertBurnParams) (int64, error) InsertCompactedLeaf(ctx context.Context, arg InsertCompactedLeafParams) error InsertLeaf(ctx context.Context, arg InsertLeafParams) error InsertNewProofEvent(ctx context.Context, arg InsertNewProofEventParams) error @@ -130,6 +131,7 @@ type Querier interface { // make the entire statement evaluate to true, if none of these extra args are // specified. QueryAssets(ctx context.Context, arg QueryAssetsParams) ([]QueryAssetsRow, error) + QueryBurns(ctx context.Context, arg QueryBurnsParams) ([]QueryBurnsRow, error) QueryEventIDs(ctx context.Context, arg QueryEventIDsParams) ([]QueryEventIDsRow, error) QueryFederationGlobalSyncConfigs(ctx context.Context) ([]FederationGlobalSyncConfig, error) // Join on mssmt_nodes to get leaf related fields. diff --git a/tapdb/sqlc/queries/transfers.sql b/tapdb/sqlc/queries/transfers.sql index c88671a45..0bcabaf5f 100644 --- a/tapdb/sqlc/queries/transfers.sql +++ b/tapdb/sqlc/queries/transfers.sql @@ -194,3 +194,33 @@ FROM passive_assets as passive JOIN managed_utxos utxos ON passive.new_anchor_utxo = utxos.utxo_id WHERE passive.transfer_id = @transfer_id; + +-- name: InsertBurn :one +INSERT INTO asset_burn_transfers ( + transfer_id, note, asset_id, group_key, amount +) +VALUES ( + @transfer_id, @note, @asset_id, @group_key, @amount +) +RETURNING burn_id; + +-- name: QueryBurns :many +SELECT + abt.note, + abt.asset_id, + abt.group_key, + abt.amount, + ct.txid AS anchor_txid -- Retrieving the txid from chain_txns. +FROM asset_burn_transfers abt +JOIN asset_transfers at ON abt.transfer_id = at.id +JOIN chain_txns ct ON at.anchor_txn_id = ct.txn_id +WHERE + -- Optionally filter by asset_id. + (abt.asset_id = @asset_id OR @asset_id IS NULL) + + -- Optionally filter by group_key. + AND (abt.group_key = @group_key OR @group_key IS NULL) + + -- Optionally filter by anchor_txid in chain_txns.txid. + AND (ct.txid = @anchor_txid OR @anchor_txid IS NULL) +ORDER BY abt.burn_id; diff --git a/tapdb/sqlc/transfers.sql.go b/tapdb/sqlc/transfers.sql.go index a5ed3aede..2bda5ebb2 100644 --- a/tapdb/sqlc/transfers.sql.go +++ b/tapdb/sqlc/transfers.sql.go @@ -356,6 +356,37 @@ func (q *Queries) InsertAssetTransferOutput(ctx context.Context, arg InsertAsset return err } +const insertBurn = `-- name: InsertBurn :one +INSERT INTO asset_burn_transfers ( + transfer_id, note, asset_id, group_key, amount +) +VALUES ( + $1, $2, $3, $4, $5 +) +RETURNING burn_id +` + +type InsertBurnParams struct { + TransferID int32 + Note sql.NullString + AssetID []byte + GroupKey []byte + Amount int64 +} + +func (q *Queries) InsertBurn(ctx context.Context, arg InsertBurnParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertBurn, + arg.TransferID, + arg.Note, + arg.AssetID, + arg.GroupKey, + arg.Amount, + ) + var burn_id int64 + err := row.Scan(&burn_id) + return burn_id, err +} + const insertPassiveAsset = `-- name: InsertPassiveAsset :exec WITH target_asset(asset_id) AS ( SELECT assets.asset_id @@ -494,6 +525,71 @@ func (q *Queries) QueryAssetTransfers(ctx context.Context, arg QueryAssetTransfe return items, nil } +const queryBurns = `-- name: QueryBurns :many +SELECT + abt.note, + abt.asset_id, + abt.group_key, + abt.amount, + ct.txid AS anchor_txid -- Retrieving the txid from chain_txns. +FROM asset_burn_transfers abt +JOIN asset_transfers at ON abt.transfer_id = at.id +JOIN chain_txns ct ON at.anchor_txn_id = ct.txn_id +WHERE + -- Optionally filter by asset_id. + (abt.asset_id = $1 OR $1 IS NULL) + + -- Optionally filter by group_key. + AND (abt.group_key = $2 OR $2 IS NULL) + + -- Optionally filter by anchor_txid in chain_txns.txid. + AND (ct.txid = $3 OR $3 IS NULL) +ORDER BY abt.burn_id +` + +type QueryBurnsParams struct { + AssetID []byte + GroupKey []byte + AnchorTxid []byte +} + +type QueryBurnsRow struct { + Note sql.NullString + AssetID []byte + GroupKey []byte + Amount int64 + AnchorTxid []byte +} + +func (q *Queries) QueryBurns(ctx context.Context, arg QueryBurnsParams) ([]QueryBurnsRow, error) { + rows, err := q.db.QueryContext(ctx, queryBurns, arg.AssetID, arg.GroupKey, arg.AnchorTxid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []QueryBurnsRow + for rows.Next() { + var i QueryBurnsRow + if err := rows.Scan( + &i.Note, + &i.AssetID, + &i.GroupKey, + &i.Amount, + &i.AnchorTxid, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const queryPassiveAssets = `-- name: QueryPassiveAssets :many SELECT passive.asset_id, passive.new_anchor_utxo, passive.script_key, passive.new_witness_stack, passive.new_proof, diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index 3974343ca..975b7f169 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -657,7 +657,7 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error { TxIndex: int32(pkg.TransferTxConfEvent.TxIndex), FinalProofs: pkg.FinalProofs, PassiveAssetProofFiles: passiveAssetProofFiles, - }) + }, nil) if err != nil { return fmt.Errorf("unable to log parcel delivery "+ "confirmation: %w", err) diff --git a/tapfreighter/interface.go b/tapfreighter/interface.go index 3105b62a6..3649b4194 100644 --- a/tapfreighter/interface.go +++ b/tapfreighter/interface.go @@ -441,7 +441,8 @@ type ExportLog interface { // LogAnchorTxConfirm updates the send package state on disk to reflect // the confirmation of the anchor transaction, ensuring the on-chain // reference information is up to date. - LogAnchorTxConfirm(context.Context, *AssetConfirmEvent) error + LogAnchorTxConfirm(context.Context, *AssetConfirmEvent, + []*AssetBurn) error // QueryParcels returns the set of confirmed or unconfirmed parcels. QueryParcels(ctx context.Context, anchorTxHash *chainhash.Hash, From c56b5597e5278232989b58594af2096148a89cfc Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 22 Nov 2024 13:11:01 +0100 Subject: [PATCH 3/6] tapfreighter: store asset burns --- rpcserver.go | 4 ++-- tapfreighter/chain_porter.go | 30 +++++++++++++++++++++++++++++- tapfreighter/parcel.go | 13 ++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index b0aae3a09..3df740775 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2275,7 +2275,7 @@ func (r *rpcServer) AnchorVirtualPsbts(ctx context.Context, } resp, err := r.cfg.ChainPorter.RequestShipment( - tapfreighter.NewPreSignedParcel(vPackets, inputCommitments), + tapfreighter.NewPreSignedParcel(vPackets, inputCommitments, ""), ) if err != nil { return nil, fmt.Errorf("error requesting delivery: %w", err) @@ -3290,7 +3290,7 @@ func (r *rpcServer) BurnAsset(ctx context.Context, resp, err := r.cfg.ChainPorter.RequestShipment( tapfreighter.NewPreSignedParcel( []*tappsbt.VPacket{fundResp.VPacket}, - fundResp.InputCommitments, + fundResp.InputCommitments, "", ), ) if err != nil { diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index 975b7f169..e58bfe671 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -646,6 +646,34 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error { ) } + // Now we scan through the VPacket for any burns. + var burns []*AssetBurn + + for _, v := range pkg.VirtualPackets { + for _, o := range v.Outputs { + if !o.Asset.IsBurn() { + continue + } + + assetID := o.Asset.ID() + + // We prepare the burn and add it to the list. + b := &AssetBurn{ + AssetID: assetID[:], + Amount: o.Amount, + AnchorTxid: pkg.OutboundPkg.AnchorTx.TxHash(), + Note: pkg.Note, + } + + if o.Asset.GroupKey != nil { + groupKey := o.Asset.GroupKey.GroupPubKey + b.GroupKey = groupKey.SerializeCompressed() + } + + burns = append(burns, b) + } + } + // At this point we have the confirmation signal, so we can mark the // parcel delivery as completed in the database. anchorTXID := pkg.OutboundPkg.AnchorTx.TxHash() @@ -657,7 +685,7 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error { TxIndex: int32(pkg.TransferTxConfEvent.TxIndex), FinalProofs: pkg.FinalProofs, PassiveAssetProofFiles: passiveAssetProofFiles, - }, nil) + }, burns) if err != nil { return fmt.Errorf("unable to log parcel delivery "+ "confirmation: %w", err) diff --git a/tapfreighter/parcel.go b/tapfreighter/parcel.go index ba86fc189..72366754b 100644 --- a/tapfreighter/parcel.go +++ b/tapfreighter/parcel.go @@ -258,6 +258,10 @@ type PreSignedParcel struct { // inputCommitments are the commitments for the input that are being // spent in the virtual transaction. inputCommitments tappsbt.InputCommitments + + // note is a string that provides any user defined description for this + // transfer. + note string } // A compile-time assertion to ensure PreSignedParcel implements the parcel @@ -266,7 +270,8 @@ var _ Parcel = (*PreSignedParcel)(nil) // NewPreSignedParcel creates a new PreSignedParcel. func NewPreSignedParcel(vPackets []*tappsbt.VPacket, - inputCommitments tappsbt.InputCommitments) *PreSignedParcel { + inputCommitments tappsbt.InputCommitments, + note string) *PreSignedParcel { return &PreSignedParcel{ parcelKit: &parcelKit{ @@ -275,6 +280,7 @@ func NewPreSignedParcel(vPackets []*tappsbt.VPacket, }, vPackets: vPackets, inputCommitments: inputCommitments, + note: note, } } @@ -290,6 +296,7 @@ func (p *PreSignedParcel) pkg() *sendPackage { SendState: SendStateAnchorSign, VirtualPackets: p.vPackets, InputCommitments: p.inputCommitments, + Note: p.note, } } @@ -464,6 +471,10 @@ type sendPackage struct { // TransferTxConfEvent contains transfer transaction on-chain // confirmation data. TransferTxConfEvent *chainntnfs.TxConfirmation + + // Note is a user provided description for this transfer. This is + // currently only used by asset burn transfers. + Note string } // ConvertToTransfer prepares the finished send data for storing to the database From 47e78750da8269663d85bba4ffc22de813569fee Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 7 Nov 2024 19:50:32 +0200 Subject: [PATCH 4/6] taprpc: track burned assets --- perms/perms.go | 4 + rpcserver.go | 37 +- taprpc/taprootassets.pb.go | 539 ++++++++++++++++++++++-------- taprpc/taprootassets.pb.gw.go | 87 +++++ taprpc/taprootassets.pb.json.go | 25 ++ taprpc/taprootassets.proto | 42 +++ taprpc/taprootassets.swagger.json | 94 ++++++ taprpc/taprootassets.yaml | 3 + taprpc/taprootassets_grpc.pb.go | 44 +++ 9 files changed, 742 insertions(+), 133 deletions(-) diff --git a/perms/perms.go b/perms/perms.go index 8fd2214f5..91bff0d6f 100644 --- a/perms/perms.go +++ b/perms/perms.go @@ -76,6 +76,10 @@ var ( Entity: "assets", Action: "write", }}, + "/taprpc.TaprootAssets/ListBurns": {{ + Entity: "assets", + Action: "read", + }}, "/taprpc.TaprootAssets/FetchAssetMeta": {{ Entity: "assets", Action: "read", diff --git a/rpcserver.go b/rpcserver.go index 3df740775..993105115 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -37,6 +37,7 @@ import ( "github.com/lightninglabs/taproot-assets/rfqmsg" "github.com/lightninglabs/taproot-assets/rpcperms" "github.com/lightninglabs/taproot-assets/tapchannel" + "github.com/lightninglabs/taproot-assets/tapdb" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tapgarden" "github.com/lightninglabs/taproot-assets/tappsbt" @@ -3290,7 +3291,7 @@ func (r *rpcServer) BurnAsset(ctx context.Context, resp, err := r.cfg.ChainPorter.RequestShipment( tapfreighter.NewPreSignedParcel( []*tappsbt.VPacket{fundResp.VPacket}, - fundResp.InputCommitments, "", + fundResp.InputCommitments, in.Note, ), ) if err != nil { @@ -3328,6 +3329,40 @@ func (r *rpcServer) BurnAsset(ctx context.Context, }, nil } +// ListBurns returns a list of burnt assets. Some filters may be defined in the +// request to return more specific results. +func (r *rpcServer) ListBurns(ctx context.Context, + in *taprpc.ListBurnsRequest) (*taprpc.ListBurnsResponse, error) { + + burns, err := r.cfg.AssetStore.QueryBurns( + ctx, tapdb.QueryBurnsFilters{ + AssetID: in.AssetId, + GroupKey: in.TweakedGroupKey, + AnchorTxid: in.AnchorTxid, + }, + ) + if err != nil { + return nil, err + } + + rpcBurns := fn.Map(burns, marshalRpcBurn) + + return &taprpc.ListBurnsResponse{ + Burns: rpcBurns, + }, nil +} + +// marshalRpcBurn creates an instance of *taprpc.AssetBurn from the tapdb model. +func marshalRpcBurn(b *tapfreighter.AssetBurn) *taprpc.AssetBurn { + return &taprpc.AssetBurn{ + Note: b.Note, + AssetId: b.AssetID, + TweakedGroupKey: b.GroupKey, + Amount: b.Amount, + AnchorTxid: b.AnchorTxid[:], + } +} + // marshalOutboundParcel turns a pending parcel into its RPC counterpart. func marshalOutboundParcel( parcel *tapfreighter.OutboundParcel) (*taprpc.AssetTransfer, diff --git a/taprpc/taprootassets.pb.go b/taprpc/taprootassets.pb.go index 15fbaf63b..8f9f753d6 100644 --- a/taprpc/taprootassets.pb.go +++ b/taprpc/taprootassets.pb.go @@ -5206,6 +5206,8 @@ type BurnAssetRequest struct { // the burn. This needs to be set to the value "assets will be destroyed" // for the burn to succeed. ConfirmationText string `protobuf:"bytes,4,opt,name=confirmation_text,json=confirmationText,proto3" json:"confirmation_text,omitempty"` + // A note that may contain user defined metadata related to this burn. + Note string `protobuf:"bytes,5,opt,name=note,proto3" json:"note,omitempty"` } func (x *BurnAssetRequest) Reset() { @@ -5275,6 +5277,13 @@ func (x *BurnAssetRequest) GetConfirmationText() string { return "" } +func (x *BurnAssetRequest) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + type isBurnAssetRequest_Asset interface { isBurnAssetRequest_Asset() } @@ -5350,6 +5359,203 @@ func (x *BurnAssetResponse) GetBurnProof() *DecodedProof { return nil } +type ListBurnsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The asset id of the burnt asset. + AssetId []byte `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // The tweaked group key of the group this asset belongs to. + TweakedGroupKey []byte `protobuf:"bytes,3,opt,name=tweaked_group_key,json=tweakedGroupKey,proto3" json:"tweaked_group_key,omitempty"` + // The txid of the transaction that the burn was anchored to. + AnchorTxid []byte `protobuf:"bytes,4,opt,name=anchor_txid,json=anchorTxid,proto3" json:"anchor_txid,omitempty"` +} + +func (x *ListBurnsRequest) Reset() { + *x = ListBurnsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListBurnsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBurnsRequest) ProtoMessage() {} + +func (x *ListBurnsRequest) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBurnsRequest.ProtoReflect.Descriptor instead. +func (*ListBurnsRequest) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{66} +} + +func (x *ListBurnsRequest) GetAssetId() []byte { + if x != nil { + return x.AssetId + } + return nil +} + +func (x *ListBurnsRequest) GetTweakedGroupKey() []byte { + if x != nil { + return x.TweakedGroupKey + } + return nil +} + +func (x *ListBurnsRequest) GetAnchorTxid() []byte { + if x != nil { + return x.AnchorTxid + } + return nil +} + +type AssetBurn struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A note that may contain user defined metadata related to this burn. + Note string `protobuf:"bytes,1,opt,name=note,proto3" json:"note,omitempty"` + // The asset id of the burnt asset. + AssetId []byte `protobuf:"bytes,2,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // The tweaked group key of the group this asset belongs to. + TweakedGroupKey []byte `protobuf:"bytes,3,opt,name=tweaked_group_key,json=tweakedGroupKey,proto3" json:"tweaked_group_key,omitempty"` + // The amount of burnt assets. + Amount uint64 `protobuf:"varint,4,opt,name=amount,proto3" json:"amount,omitempty"` + // The txid of the transaction that the burn was anchored to. + AnchorTxid []byte `protobuf:"bytes,5,opt,name=anchor_txid,json=anchorTxid,proto3" json:"anchor_txid,omitempty"` +} + +func (x *AssetBurn) Reset() { + *x = AssetBurn{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssetBurn) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssetBurn) ProtoMessage() {} + +func (x *AssetBurn) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[67] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssetBurn.ProtoReflect.Descriptor instead. +func (*AssetBurn) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{67} +} + +func (x *AssetBurn) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +func (x *AssetBurn) GetAssetId() []byte { + if x != nil { + return x.AssetId + } + return nil +} + +func (x *AssetBurn) GetTweakedGroupKey() []byte { + if x != nil { + return x.TweakedGroupKey + } + return nil +} + +func (x *AssetBurn) GetAmount() uint64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *AssetBurn) GetAnchorTxid() []byte { + if x != nil { + return x.AnchorTxid + } + return nil +} + +type ListBurnsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Burns []*AssetBurn `protobuf:"bytes,1,rep,name=burns,proto3" json:"burns,omitempty"` +} + +func (x *ListBurnsResponse) Reset() { + *x = ListBurnsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListBurnsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBurnsResponse) ProtoMessage() {} + +func (x *ListBurnsResponse) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[68] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBurnsResponse.ProtoReflect.Descriptor instead. +func (*ListBurnsResponse) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{68} +} + +func (x *ListBurnsResponse) GetBurns() []*AssetBurn { + if x != nil { + return x.Burns + } + return nil +} + type OutPoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5364,7 +5570,7 @@ type OutPoint struct { func (x *OutPoint) Reset() { *x = OutPoint{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[66] + mi := &file_taprootassets_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5377,7 +5583,7 @@ func (x *OutPoint) String() string { func (*OutPoint) ProtoMessage() {} func (x *OutPoint) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[66] + mi := &file_taprootassets_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5390,7 +5596,7 @@ func (x *OutPoint) ProtoReflect() protoreflect.Message { // Deprecated: Use OutPoint.ProtoReflect.Descriptor instead. func (*OutPoint) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{66} + return file_taprootassets_proto_rawDescGZIP(), []int{69} } func (x *OutPoint) GetTxid() []byte { @@ -5423,7 +5629,7 @@ type SubscribeReceiveEventsRequest struct { func (x *SubscribeReceiveEventsRequest) Reset() { *x = SubscribeReceiveEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[67] + mi := &file_taprootassets_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5436,7 +5642,7 @@ func (x *SubscribeReceiveEventsRequest) String() string { func (*SubscribeReceiveEventsRequest) ProtoMessage() {} func (x *SubscribeReceiveEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[67] + mi := &file_taprootassets_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5449,7 +5655,7 @@ func (x *SubscribeReceiveEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeReceiveEventsRequest.ProtoReflect.Descriptor instead. func (*SubscribeReceiveEventsRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{67} + return file_taprootassets_proto_rawDescGZIP(), []int{70} } func (x *SubscribeReceiveEventsRequest) GetFilterAddr() string { @@ -5491,7 +5697,7 @@ type ReceiveEvent struct { func (x *ReceiveEvent) Reset() { *x = ReceiveEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[68] + mi := &file_taprootassets_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5504,7 +5710,7 @@ func (x *ReceiveEvent) String() string { func (*ReceiveEvent) ProtoMessage() {} func (x *ReceiveEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[68] + mi := &file_taprootassets_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5517,7 +5723,7 @@ func (x *ReceiveEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ReceiveEvent.ProtoReflect.Descriptor instead. func (*ReceiveEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{68} + return file_taprootassets_proto_rawDescGZIP(), []int{71} } func (x *ReceiveEvent) GetTimestamp() int64 { @@ -5575,7 +5781,7 @@ type SubscribeSendEventsRequest struct { func (x *SubscribeSendEventsRequest) Reset() { *x = SubscribeSendEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[69] + mi := &file_taprootassets_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5588,7 +5794,7 @@ func (x *SubscribeSendEventsRequest) String() string { func (*SubscribeSendEventsRequest) ProtoMessage() {} func (x *SubscribeSendEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[69] + mi := &file_taprootassets_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5601,7 +5807,7 @@ func (x *SubscribeSendEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeSendEventsRequest.ProtoReflect.Descriptor instead. func (*SubscribeSendEventsRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{69} + return file_taprootassets_proto_rawDescGZIP(), []int{72} } func (x *SubscribeSendEventsRequest) GetFilterScriptKey() []byte { @@ -5647,7 +5853,7 @@ type SendEvent struct { func (x *SendEvent) Reset() { *x = SendEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[70] + mi := &file_taprootassets_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5660,7 +5866,7 @@ func (x *SendEvent) String() string { func (*SendEvent) ProtoMessage() {} func (x *SendEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[70] + mi := &file_taprootassets_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5673,7 +5879,7 @@ func (x *SendEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEvent.ProtoReflect.Descriptor instead. func (*SendEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{70} + return file_taprootassets_proto_rawDescGZIP(), []int{73} } func (x *SendEvent) GetTimestamp() int64 { @@ -5763,7 +5969,7 @@ type AnchorTransaction struct { func (x *AnchorTransaction) Reset() { *x = AnchorTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[71] + mi := &file_taprootassets_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5776,7 +5982,7 @@ func (x *AnchorTransaction) String() string { func (*AnchorTransaction) ProtoMessage() {} func (x *AnchorTransaction) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[71] + mi := &file_taprootassets_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5789,7 +5995,7 @@ func (x *AnchorTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use AnchorTransaction.ProtoReflect.Descriptor instead. func (*AnchorTransaction) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{71} + return file_taprootassets_proto_rawDescGZIP(), []int{74} } func (x *AnchorTransaction) GetAnchorPsbt() []byte { @@ -6493,7 +6699,7 @@ var file_taprootassets_proto_rawDesc = []byte{ 0x65, 0x74, 0x49, 0x64, 0x53, 0x74, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x42, 0x07, 0x0a, - 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xaf, 0x01, 0x0a, 0x10, 0x42, 0x75, 0x72, 0x6e, 0x41, + 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xc3, 0x01, 0x0a, 0x10, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, @@ -6503,16 +6709,39 @@ var file_taprootassets_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, 0x74, 0x42, - 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x84, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x72, - 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, - 0x0a, 0x0d, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x0c, 0x62, 0x75, - 0x72, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x0a, 0x62, 0x75, - 0x72, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x09, 0x62, 0x75, 0x72, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x6f, 0x74, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x84, 0x01, 0x0a, + 0x11, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x52, 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x33, + 0x0a, 0x0a, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x09, 0x62, 0x75, 0x72, 0x6e, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x22, 0x7a, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x74, + 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x1f, + 0x0a, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x54, 0x78, 0x69, 0x64, 0x22, + 0x9f, 0x01, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, + 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x54, 0x78, 0x69, + 0x64, 0x22, 0x3c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x62, 0x75, 0x72, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x52, 0x05, 0x62, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x41, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, @@ -6654,7 +6883,7 @@ var file_taprootassets_proto_rawDesc = []byte{ 0x4e, 0x45, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x43, 0x45, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x41, 0x52, 0x43, 0x45, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x52, - 0x45, 0x5f, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x45, 0x44, 0x10, 0x03, 0x32, 0xd8, 0x0a, 0x0a, + 0x45, 0x5f, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x45, 0x44, 0x10, 0x03, 0x32, 0x9a, 0x0b, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, @@ -6721,30 +6950,34 @@ var file_taprootassets_proto_rawDesc = []byte{ 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x72, - 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x46, 0x65, - 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x2e, 0x74, - 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x57, - 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x22, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, - 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, + 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x42, 0x75, 0x72, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0e, + 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, + 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, + 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x57, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x74, 0x61, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x13, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x22, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -6760,7 +6993,7 @@ func file_taprootassets_proto_rawDescGZIP() []byte { } var file_taprootassets_proto_enumTypes = make([]protoimpl.EnumInfo, 9) -var file_taprootassets_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_taprootassets_proto_msgTypes = make([]protoimpl.MessageInfo, 79) var file_taprootassets_proto_goTypes = []interface{}{ (AssetType)(0), // 0: taprpc.AssetType (AssetMetaType)(0), // 1: taprpc.AssetMetaType @@ -6837,16 +7070,19 @@ var file_taprootassets_proto_goTypes = []interface{}{ (*FetchAssetMetaRequest)(nil), // 72: taprpc.FetchAssetMetaRequest (*BurnAssetRequest)(nil), // 73: taprpc.BurnAssetRequest (*BurnAssetResponse)(nil), // 74: taprpc.BurnAssetResponse - (*OutPoint)(nil), // 75: taprpc.OutPoint - (*SubscribeReceiveEventsRequest)(nil), // 76: taprpc.SubscribeReceiveEventsRequest - (*ReceiveEvent)(nil), // 77: taprpc.ReceiveEvent - (*SubscribeSendEventsRequest)(nil), // 78: taprpc.SubscribeSendEventsRequest - (*SendEvent)(nil), // 79: taprpc.SendEvent - (*AnchorTransaction)(nil), // 80: taprpc.AnchorTransaction - nil, // 81: taprpc.ListUtxosResponse.ManagedUtxosEntry - nil, // 82: taprpc.ListGroupsResponse.GroupsEntry - nil, // 83: taprpc.ListBalancesResponse.AssetBalancesEntry - nil, // 84: taprpc.ListBalancesResponse.AssetGroupBalancesEntry + (*ListBurnsRequest)(nil), // 75: taprpc.ListBurnsRequest + (*AssetBurn)(nil), // 76: taprpc.AssetBurn + (*ListBurnsResponse)(nil), // 77: taprpc.ListBurnsResponse + (*OutPoint)(nil), // 78: taprpc.OutPoint + (*SubscribeReceiveEventsRequest)(nil), // 79: taprpc.SubscribeReceiveEventsRequest + (*ReceiveEvent)(nil), // 80: taprpc.ReceiveEvent + (*SubscribeSendEventsRequest)(nil), // 81: taprpc.SubscribeSendEventsRequest + (*SendEvent)(nil), // 82: taprpc.SendEvent + (*AnchorTransaction)(nil), // 83: taprpc.AnchorTransaction + nil, // 84: taprpc.ListUtxosResponse.ManagedUtxosEntry + nil, // 85: taprpc.ListGroupsResponse.GroupsEntry + nil, // 86: taprpc.ListBalancesResponse.AssetBalancesEntry + nil, // 87: taprpc.ListBalancesResponse.AssetGroupBalancesEntry } var file_taprootassets_proto_depIdxs = []int32{ 1, // 0: taprpc.AssetMeta.type:type_name -> taprpc.AssetMetaType @@ -6866,14 +7102,14 @@ var file_taprootassets_proto_depIdxs = []int32{ 21, // 14: taprpc.SplitCommitment.root_asset:type_name -> taprpc.Asset 21, // 15: taprpc.ListAssetResponse.assets:type_name -> taprpc.Asset 21, // 16: taprpc.ManagedUtxo.assets:type_name -> taprpc.Asset - 81, // 17: taprpc.ListUtxosResponse.managed_utxos:type_name -> taprpc.ListUtxosResponse.ManagedUtxosEntry + 84, // 17: taprpc.ListUtxosResponse.managed_utxos:type_name -> taprpc.ListUtxosResponse.ManagedUtxosEntry 0, // 18: taprpc.AssetHumanReadable.type:type_name -> taprpc.AssetType 2, // 19: taprpc.AssetHumanReadable.version:type_name -> taprpc.AssetVersion 29, // 20: taprpc.GroupedAssets.assets:type_name -> taprpc.AssetHumanReadable - 82, // 21: taprpc.ListGroupsResponse.groups:type_name -> taprpc.ListGroupsResponse.GroupsEntry + 85, // 21: taprpc.ListGroupsResponse.groups:type_name -> taprpc.ListGroupsResponse.GroupsEntry 12, // 22: taprpc.AssetBalance.asset_genesis:type_name -> taprpc.GenesisInfo - 83, // 23: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry - 84, // 24: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry + 86, // 23: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry + 87, // 24: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry 39, // 25: taprpc.ListTransfersResponse.transfers:type_name -> taprpc.AssetTransfer 40, // 26: taprpc.AssetTransfer.inputs:type_name -> taprpc.TransferInput 42, // 27: taprpc.AssetTransfer.outputs:type_name -> taprpc.TransferOutput @@ -6899,7 +7135,7 @@ var file_taprootassets_proto_depIdxs = []int32{ 18, // 47: taprpc.DecodedProof.group_key_reveal:type_name -> taprpc.GroupKeyReveal 59, // 48: taprpc.VerifyProofResponse.decoded_proof:type_name -> taprpc.DecodedProof 59, // 49: taprpc.DecodeProofResponse.decoded_proof:type_name -> taprpc.DecodedProof - 75, // 50: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint + 78, // 50: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint 47, // 51: taprpc.AddrEvent.addr:type_name -> taprpc.Addr 6, // 52: taprpc.AddrEvent.status:type_name -> taprpc.AddrEventStatus 6, // 53: taprpc.AddrReceivesRequest.filter_status:type_name -> taprpc.AddrEventStatus @@ -6907,62 +7143,65 @@ var file_taprootassets_proto_depIdxs = []int32{ 39, // 55: taprpc.SendAssetResponse.transfer:type_name -> taprpc.AssetTransfer 39, // 56: taprpc.BurnAssetResponse.burn_transfer:type_name -> taprpc.AssetTransfer 59, // 57: taprpc.BurnAssetResponse.burn_proof:type_name -> taprpc.DecodedProof - 47, // 58: taprpc.ReceiveEvent.address:type_name -> taprpc.Addr - 6, // 59: taprpc.ReceiveEvent.status:type_name -> taprpc.AddrEventStatus - 8, // 60: taprpc.SendEvent.parcel_type:type_name -> taprpc.ParcelType - 47, // 61: taprpc.SendEvent.addresses:type_name -> taprpc.Addr - 80, // 62: taprpc.SendEvent.anchor_transaction:type_name -> taprpc.AnchorTransaction - 39, // 63: taprpc.SendEvent.transfer:type_name -> taprpc.AssetTransfer - 75, // 64: taprpc.AnchorTransaction.lnd_locked_utxos:type_name -> taprpc.OutPoint - 26, // 65: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo - 30, // 66: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets - 33, // 67: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance - 34, // 68: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance - 10, // 69: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest - 25, // 70: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest - 28, // 71: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest - 32, // 72: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest - 36, // 73: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest - 43, // 74: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest - 45, // 75: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest - 48, // 76: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest - 50, // 77: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest - 57, // 78: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest - 65, // 79: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest - 58, // 80: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile - 61, // 81: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest - 63, // 82: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest - 67, // 83: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest - 73, // 84: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest - 70, // 85: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest - 72, // 86: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest - 76, // 87: taprpc.TaprootAssets.SubscribeReceiveEvents:input_type -> taprpc.SubscribeReceiveEventsRequest - 78, // 88: taprpc.TaprootAssets.SubscribeSendEvents:input_type -> taprpc.SubscribeSendEventsRequest - 24, // 89: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse - 27, // 90: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse - 31, // 91: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse - 35, // 92: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse - 37, // 93: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse - 44, // 94: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse - 46, // 95: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse - 49, // 96: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse - 47, // 97: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr - 47, // 98: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr - 66, // 99: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse - 60, // 100: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse - 62, // 101: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse - 58, // 102: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile - 69, // 103: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse - 74, // 104: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse - 71, // 105: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse - 9, // 106: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.AssetMeta - 77, // 107: taprpc.TaprootAssets.SubscribeReceiveEvents:output_type -> taprpc.ReceiveEvent - 79, // 108: taprpc.TaprootAssets.SubscribeSendEvents:output_type -> taprpc.SendEvent - 89, // [89:109] is the sub-list for method output_type - 69, // [69:89] is the sub-list for method input_type - 69, // [69:69] is the sub-list for extension type_name - 69, // [69:69] is the sub-list for extension extendee - 0, // [0:69] is the sub-list for field type_name + 76, // 58: taprpc.ListBurnsResponse.burns:type_name -> taprpc.AssetBurn + 47, // 59: taprpc.ReceiveEvent.address:type_name -> taprpc.Addr + 6, // 60: taprpc.ReceiveEvent.status:type_name -> taprpc.AddrEventStatus + 8, // 61: taprpc.SendEvent.parcel_type:type_name -> taprpc.ParcelType + 47, // 62: taprpc.SendEvent.addresses:type_name -> taprpc.Addr + 83, // 63: taprpc.SendEvent.anchor_transaction:type_name -> taprpc.AnchorTransaction + 39, // 64: taprpc.SendEvent.transfer:type_name -> taprpc.AssetTransfer + 78, // 65: taprpc.AnchorTransaction.lnd_locked_utxos:type_name -> taprpc.OutPoint + 26, // 66: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo + 30, // 67: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets + 33, // 68: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance + 34, // 69: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance + 10, // 70: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest + 25, // 71: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest + 28, // 72: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest + 32, // 73: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest + 36, // 74: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest + 43, // 75: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest + 45, // 76: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest + 48, // 77: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest + 50, // 78: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest + 57, // 79: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest + 65, // 80: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest + 58, // 81: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile + 61, // 82: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest + 63, // 83: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest + 67, // 84: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest + 73, // 85: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest + 75, // 86: taprpc.TaprootAssets.ListBurns:input_type -> taprpc.ListBurnsRequest + 70, // 87: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest + 72, // 88: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest + 79, // 89: taprpc.TaprootAssets.SubscribeReceiveEvents:input_type -> taprpc.SubscribeReceiveEventsRequest + 81, // 90: taprpc.TaprootAssets.SubscribeSendEvents:input_type -> taprpc.SubscribeSendEventsRequest + 24, // 91: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse + 27, // 92: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse + 31, // 93: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse + 35, // 94: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse + 37, // 95: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse + 44, // 96: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse + 46, // 97: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse + 49, // 98: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse + 47, // 99: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr + 47, // 100: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr + 66, // 101: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse + 60, // 102: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse + 62, // 103: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse + 58, // 104: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile + 69, // 105: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse + 74, // 106: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse + 77, // 107: taprpc.TaprootAssets.ListBurns:output_type -> taprpc.ListBurnsResponse + 71, // 108: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse + 9, // 109: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.AssetMeta + 80, // 110: taprpc.TaprootAssets.SubscribeReceiveEvents:output_type -> taprpc.ReceiveEvent + 82, // 111: taprpc.TaprootAssets.SubscribeSendEvents:output_type -> taprpc.SendEvent + 91, // [91:112] is the sub-list for method output_type + 70, // [70:91] is the sub-list for method input_type + 70, // [70:70] is the sub-list for extension type_name + 70, // [70:70] is the sub-list for extension extendee + 0, // [0:70] is the sub-list for field type_name } func init() { file_taprootassets_proto_init() } @@ -7764,7 +8003,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OutPoint); i { + switch v := v.(*ListBurnsRequest); i { case 0: return &v.state case 1: @@ -7776,7 +8015,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeReceiveEventsRequest); i { + switch v := v.(*AssetBurn); i { case 0: return &v.state case 1: @@ -7788,7 +8027,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReceiveEvent); i { + switch v := v.(*ListBurnsResponse); i { case 0: return &v.state case 1: @@ -7800,7 +8039,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeSendEventsRequest); i { + switch v := v.(*OutPoint); i { case 0: return &v.state case 1: @@ -7812,7 +8051,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEvent); i { + switch v := v.(*SubscribeReceiveEventsRequest); i { case 0: return &v.state case 1: @@ -7824,6 +8063,42 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReceiveEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeSendEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AnchorTransaction); i { case 0: return &v.state @@ -7856,7 +8131,7 @@ func file_taprootassets_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_taprootassets_proto_rawDesc, NumEnums: 9, - NumMessages: 76, + NumMessages: 79, NumExtensions: 0, NumServices: 1, }, diff --git a/taprpc/taprootassets.pb.gw.go b/taprpc/taprootassets.pb.gw.go index ced6f11be..173bc1556 100644 --- a/taprpc/taprootassets.pb.gw.go +++ b/taprpc/taprootassets.pb.gw.go @@ -621,6 +621,42 @@ func local_request_TaprootAssets_BurnAsset_0(ctx context.Context, marshaler runt } +var ( + filter_TaprootAssets_ListBurns_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_TaprootAssets_ListBurns_0(ctx context.Context, marshaler runtime.Marshaler, client TaprootAssetsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListBurnsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TaprootAssets_ListBurns_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListBurns(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TaprootAssets_ListBurns_0(ctx context.Context, marshaler runtime.Marshaler, server TaprootAssetsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListBurnsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TaprootAssets_ListBurns_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListBurns(ctx, &protoReq) + return msg, metadata, err + +} + func request_TaprootAssets_GetInfo_0(ctx context.Context, marshaler runtime.Marshaler, client TaprootAssetsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GetInfoRequest var metadata runtime.ServerMetadata @@ -1280,6 +1316,31 @@ func RegisterTaprootAssetsHandlerServer(ctx context.Context, mux *runtime.ServeM }) + mux.Handle("GET", pattern_TaprootAssets_ListBurns_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/taprpc.TaprootAssets/ListBurns", runtime.WithHTTPPathPattern("/v1/taproot-assets/burns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TaprootAssets_ListBurns_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_TaprootAssets_ListBurns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_TaprootAssets_GetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1784,6 +1845,28 @@ func RegisterTaprootAssetsHandlerClient(ctx context.Context, mux *runtime.ServeM }) + mux.Handle("GET", pattern_TaprootAssets_ListBurns_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/taprpc.TaprootAssets/ListBurns", runtime.WithHTTPPathPattern("/v1/taproot-assets/burns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TaprootAssets_ListBurns_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_TaprootAssets_ListBurns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_TaprootAssets_GetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1932,6 +2015,8 @@ var ( pattern_TaprootAssets_BurnAsset_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "taproot-assets", "burn"}, "")) + pattern_TaprootAssets_ListBurns_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "taproot-assets", "burns"}, "")) + pattern_TaprootAssets_GetInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "taproot-assets", "getinfo"}, "")) pattern_TaprootAssets_FetchAssetMeta_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"v1", "taproot-assets", "assets", "meta", "asset-id", "asset_id_str"}, "")) @@ -1978,6 +2063,8 @@ var ( forward_TaprootAssets_BurnAsset_0 = runtime.ForwardResponseMessage + forward_TaprootAssets_ListBurns_0 = runtime.ForwardResponseMessage + forward_TaprootAssets_GetInfo_0 = runtime.ForwardResponseMessage forward_TaprootAssets_FetchAssetMeta_0 = runtime.ForwardResponseMessage diff --git a/taprpc/taprootassets.pb.json.go b/taprpc/taprootassets.pb.json.go index fc6a2904f..ad66887e3 100644 --- a/taprpc/taprootassets.pb.json.go +++ b/taprpc/taprootassets.pb.json.go @@ -421,6 +421,31 @@ func RegisterTaprootAssetsJSONCallbacks(registry map[string]func(ctx context.Con callback(string(respBytes), nil) } + registry["taprpc.TaprootAssets.ListBurns"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &ListBurnsRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewTaprootAssetsClient(conn) + resp, err := client.ListBurns(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + registry["taprpc.TaprootAssets.GetInfo"] = func(ctx context.Context, conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { diff --git a/taprpc/taprootassets.proto b/taprpc/taprootassets.proto index f591082b7..84d54e767 100644 --- a/taprpc/taprootassets.proto +++ b/taprpc/taprootassets.proto @@ -104,6 +104,13 @@ service TaprootAssets { */ rpc BurnAsset (BurnAssetRequest) returns (BurnAssetResponse); + /* tapcli: `assets listburns` + ListBurns lists the asset burns that this wallet has performed. These assets + are not recoverable in any way. Filters may be applied to return more + specific results. + */ + rpc ListBurns (ListBurnsRequest) returns (ListBurnsResponse); + /* tapcli: `getinfo` GetInfo returns the information for the node. */ @@ -1202,6 +1209,9 @@ message BurnAssetRequest { // the burn. This needs to be set to the value "assets will be destroyed" // for the burn to succeed. string confirmation_text = 4; + + // A note that may contain user defined metadata related to this burn. + string note = 5; } message BurnAssetResponse { @@ -1212,6 +1222,38 @@ message BurnAssetResponse { DecodedProof burn_proof = 2; } +message ListBurnsRequest { + // The asset id of the burnt asset. + bytes asset_id = 1; + + // The tweaked group key of the group this asset belongs to. + bytes tweaked_group_key = 3; + + // The txid of the transaction that the burn was anchored to. + bytes anchor_txid = 4; +} + +message AssetBurn { + // A note that may contain user defined metadata related to this burn. + string note = 1; + + // The asset id of the burnt asset. + bytes asset_id = 2; + + // The tweaked group key of the group this asset belongs to. + bytes tweaked_group_key = 3; + + // The amount of burnt assets. + uint64 amount = 4; + + // The txid of the transaction that the burn was anchored to. + bytes anchor_txid = 5; +} + +message ListBurnsResponse { + repeated AssetBurn burns = 1; +} + message OutPoint { /* Raw bytes representing the transaction id. diff --git a/taprpc/taprootassets.swagger.json b/taprpc/taprootassets.swagger.json index 37bb3f60a..91c664f32 100644 --- a/taprpc/taprootassets.swagger.json +++ b/taprpc/taprootassets.swagger.json @@ -543,6 +543,55 @@ ] } }, + "/v1/taproot-assets/burns": { + "get": { + "summary": "tapcli: `assets listburns`\nListBurns lists the asset burns that this wallet has performed. These assets\nare not recoverable in any way. Filters may be applied to return more\nspecific results.", + "operationId": "TaprootAssets_ListBurns", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/taprpcListBurnsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "asset_id", + "description": "The asset id of the burnt asset.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "tweaked_group_key", + "description": "The tweaked group key of the group this asset belongs to.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "anchor_txid", + "description": "The txid of the transaction that the burn was anchored to.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "TaprootAssets" + ] + } + }, "/v1/taproot-assets/debuglevel": { "post": { "summary": "tapcli: `debuglevel`\nDebugLevel allows a caller to programmatically set the logging verbosity of\ntapd. The logging can be targeted according to a coarse daemon-wide logging\nlevel, or in a granular fashion to specify the logging for a target\nsub-system.", @@ -1202,6 +1251,35 @@ } } }, + "taprpcAssetBurn": { + "type": "object", + "properties": { + "note": { + "type": "string", + "description": "A note that may contain user defined metadata related to this burn." + }, + "asset_id": { + "type": "string", + "format": "byte", + "description": "The asset id of the burnt asset." + }, + "tweaked_group_key": { + "type": "string", + "format": "byte", + "description": "The tweaked group key of the group this asset belongs to." + }, + "amount": { + "type": "string", + "format": "uint64", + "description": "The amount of burnt assets." + }, + "anchor_txid": { + "type": "string", + "format": "byte", + "description": "The txid of the transaction that the burn was anchored to." + } + } + }, "taprpcAssetGroup": { "type": "object", "properties": { @@ -1391,6 +1469,10 @@ "confirmation_text": { "type": "string", "description": "A safety check to ensure the user is aware of the destructive nature of\nthe burn. This needs to be set to the value \"assets will be destroyed\"\nfor the burn to succeed." + }, + "note": { + "type": "string", + "description": "A note that may contain user defined metadata related to this burn." } } }, @@ -1745,6 +1827,18 @@ } } }, + "taprpcListBurnsResponse": { + "type": "object", + "properties": { + "burns": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/taprpcAssetBurn" + } + } + } + }, "taprpcListGroupsResponse": { "type": "object", "properties": { diff --git a/taprpc/taprootassets.yaml b/taprpc/taprootassets.yaml index bb364586a..e570bb99c 100644 --- a/taprpc/taprootassets.yaml +++ b/taprpc/taprootassets.yaml @@ -61,6 +61,9 @@ http: post: "/v1/taproot-assets/burn" body: "*" + - selector: taprpc.TaprootAssets.ListBurns + get: "/v1/taproot-assets/burns" + - selector: taprpc.TaprootAssets.ListTransfers get: "/v1/taproot-assets/assets/transfers" additional_bindings: diff --git a/taprpc/taprootassets_grpc.pb.go b/taprpc/taprootassets_grpc.pb.go index f7a44b3ca..b74336743 100644 --- a/taprpc/taprootassets_grpc.pb.go +++ b/taprpc/taprootassets_grpc.pb.go @@ -85,6 +85,11 @@ type TaprootAssetsClient interface { // burning is such a destructive and non-reversible operation, some specific // values need to be set in the request to avoid accidental burns. BurnAsset(ctx context.Context, in *BurnAssetRequest, opts ...grpc.CallOption) (*BurnAssetResponse, error) + // tapcli: `assets listburns` + // ListBurns lists the asset burns that this wallet has performed. These assets + // are not recoverable in any way. Filters may be applied to return more + // specific results. + ListBurns(ctx context.Context, in *ListBurnsRequest, opts ...grpc.CallOption) (*ListBurnsResponse, error) // tapcli: `getinfo` // GetInfo returns the information for the node. GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) @@ -254,6 +259,15 @@ func (c *taprootAssetsClient) BurnAsset(ctx context.Context, in *BurnAssetReques return out, nil } +func (c *taprootAssetsClient) ListBurns(ctx context.Context, in *ListBurnsRequest, opts ...grpc.CallOption) (*ListBurnsResponse, error) { + out := new(ListBurnsResponse) + err := c.cc.Invoke(ctx, "/taprpc.TaprootAssets/ListBurns", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *taprootAssetsClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) { out := new(GetInfoResponse) err := c.cc.Invoke(ctx, "/taprpc.TaprootAssets/GetInfo", in, out, opts...) @@ -407,6 +421,11 @@ type TaprootAssetsServer interface { // burning is such a destructive and non-reversible operation, some specific // values need to be set in the request to avoid accidental burns. BurnAsset(context.Context, *BurnAssetRequest) (*BurnAssetResponse, error) + // tapcli: `assets listburns` + // ListBurns lists the asset burns that this wallet has performed. These assets + // are not recoverable in any way. Filters may be applied to return more + // specific results. + ListBurns(context.Context, *ListBurnsRequest) (*ListBurnsResponse, error) // tapcli: `getinfo` // GetInfo returns the information for the node. GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) @@ -477,6 +496,9 @@ func (UnimplementedTaprootAssetsServer) SendAsset(context.Context, *SendAssetReq func (UnimplementedTaprootAssetsServer) BurnAsset(context.Context, *BurnAssetRequest) (*BurnAssetResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method BurnAsset not implemented") } +func (UnimplementedTaprootAssetsServer) ListBurns(context.Context, *ListBurnsRequest) (*ListBurnsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListBurns not implemented") +} func (UnimplementedTaprootAssetsServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") } @@ -790,6 +812,24 @@ func _TaprootAssets_BurnAsset_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _TaprootAssets_ListBurns_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListBurnsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TaprootAssetsServer).ListBurns(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/taprpc.TaprootAssets/ListBurns", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TaprootAssetsServer).ListBurns(ctx, req.(*ListBurnsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _TaprootAssets_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetInfoRequest) if err := dec(in); err != nil { @@ -939,6 +979,10 @@ var TaprootAssets_ServiceDesc = grpc.ServiceDesc{ MethodName: "BurnAsset", Handler: _TaprootAssets_BurnAsset_Handler, }, + { + MethodName: "ListBurns", + Handler: _TaprootAssets_ListBurns_Handler, + }, { MethodName: "GetInfo", Handler: _TaprootAssets_GetInfo_Handler, From a21e4eb9d4306f3b6936bd7c3af43b1dc30bda97 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 7 Nov 2024 20:02:41 +0200 Subject: [PATCH 5/6] itest: use ListBurns in burn test --- itest/burn_test.go | 91 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/itest/burn_test.go b/itest/burn_test.go index 9a4f61b1e..d4f597d05 100644 --- a/itest/burn_test.go +++ b/itest/burn_test.go @@ -1,6 +1,7 @@ package itest import ( + "bytes" "context" "encoding/hex" @@ -129,12 +130,17 @@ func testBurnAssets(t *harnessTest) { // Test case 2: We'll now try to burn a small amount of assets, which // should select the largest output, which is located alone in an anchor // output. - const burnAmt = 100 + const ( + burnAmt = 100 + burnNote = "blazeit" + ) + burnResp, err := t.tapd.BurnAsset(ctxt, &taprpc.BurnAssetRequest{ Asset: &taprpc.BurnAssetRequest_AssetId{ AssetId: simpleAssetID[:], }, AmountToBurn: burnAmt, + Note: burnNote, ConfirmationText: taprootassets.AssetBurnConfirmationText, }) require.NoError(t.t, err) @@ -169,6 +175,16 @@ func testBurnAssets(t *harnessTest) { t.t, t.tapd, simpleAssetGen.AssetId, simpleAsset.Amount-burnAmt, ) + burns, err := t.tapd.ListBurns(ctxt, &taprpc.ListBurnsRequest{}) + require.NoError(t.t, err) + + require.Len(t.t, burns.Burns, 1) + burn := burns.Burns[0] + require.Equal(t.t, uint64(burnAmt), burn.Amount) + require.Equal(t.t, burnResp.BurnTransfer.AnchorTxHash, burn.AnchorTxid) + require.Equal(t.t, burn.AssetId, simpleAssetID[:]) + require.Equal(t.t, burn.Note, burnNote) + // The burned asset should be pruned from the tree when we next spend // the anchor output it was in (together with the change). So let's test // that we can successfully spend the change output. @@ -280,6 +296,35 @@ func testBurnAssets(t *harnessTest) { t.t, t.tapd, simpleGroupGen.AssetId, simpleGroup.Amount-burnAmt, ) + burns, err = t.tapd.ListBurns(ctxt, &taprpc.ListBurnsRequest{}) + require.NoError(t.t, err) + + require.Len(t.t, burns.Burns, 4) + var groupBurn *taprpc.AssetBurn + for _, b := range burns.Burns { + if bytes.Equal(b.AssetId, simpleGroupGen.AssetId) { + groupBurn = b + } + } + + // Keep track of the txhash of the anchor transaction that completed + // this transfer. This will be used later to query burns with a txhash + // filter. + groupBurnTxHash := burnResp.BurnTransfer.AnchorTxHash + + require.Equal(t.t, uint64(burnAmt), groupBurn.Amount) + require.Equal( + t.t, burnResp.BurnTransfer.AnchorTxHash, groupBurn.AnchorTxid, + ) + + require.Equal(t.t, groupBurn.AssetId, simpleGroupGen.AssetId[:]) + require.Equal( + t.t, groupBurn.TweakedGroupKey, + simpleGroup.AssetGroup.TweakedGroupKey, + ) + + require.Equal(t.t, groupBurn.Note, "") + // Test case 6: Burn the single unit of a grouped collectible. We start // by making sure we still have the full balance before burning. AssertBalanceByID( @@ -305,6 +350,36 @@ func testBurnAssets(t *harnessTest) { simpleGroupCollectGen.AssetId, []uint64{1}, 6, 7, 1, true, ) AssertBalanceByID(t.t, t.tapd, simpleGroupCollectGen.AssetId, 0) + + // We now perform some queries to test the filters of the ListBurns + // call. + + // Fetch the burns related to the simple asset id, which should have a + // total of 2 burns (tc1 & tc4). + burns, err = t.tapd.ListBurns(ctxt, &taprpc.ListBurnsRequest{ + AssetId: simpleAssetGen.AssetId, + }) + require.NoError(t.t, err) + + require.Len(t.t, burns.Burns, 2) + + // Fetch the burns related to the group key of the grouped asset in tc5. + // There should be 1 burn. + burns, err = t.tapd.ListBurns(ctxt, &taprpc.ListBurnsRequest{ + TweakedGroupKey: simpleGroup.AssetGroup.TweakedGroupKey, + }) + require.NoError(t.t, err) + + require.Len(t.t, burns.Burns, 1) + + // Fetch the burns associated with the txhash of the burn in tc5. There + // should be 1 burn returned. + burns, err = t.tapd.ListBurns(ctxt, &taprpc.ListBurnsRequest{ + AnchorTxid: groupBurnTxHash, + }) + require.NoError(t.t, err) + + require.Len(t.t, burns.Burns, 1) } // testBurnGroupedAssets tests that some amount of an asset from an asset group @@ -315,6 +390,7 @@ func testBurnGroupedAssets(t *harnessTest) { miner = t.lndHarness.Miner().Client firstMintReq = issuableAssets[0] + burnNote = "blazeit" ) // We start off without any asset groups. @@ -376,6 +452,7 @@ func testBurnGroupedAssets(t *harnessTest) { AssetId: burnAssetID, }, AmountToBurn: burnAmt, + Note: burnNote, ConfirmationText: taprootassets.AssetBurnConfirmationText, }) require.NoError(t.t, err) @@ -414,4 +491,16 @@ func testBurnGroupedAssets(t *harnessTest) { encodedGroupKey = hex.EncodeToString(assetGroupKey) assetGroup = assetGroups.Groups[encodedGroupKey] require.Len(t.t, assetGroup.Assets, 2) + + burns, err := t.tapd.ListBurns(ctxb, &taprpc.ListBurnsRequest{ + TweakedGroupKey: assetGroupKey, + }) + require.NoError(t.t, err) + require.Len(t.t, burns.Burns, 1) + + burn := burns.Burns[0] + + require.Equal(t.t, burnAmt, burn.Amount) + require.Equal(t.t, burnNote, burn.Note) + require.Equal(t.t, assetGroupKey, burn.TweakedGroupKey) } From e9364e0ce121ae0c975fc2d3499b830fd3535d10 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 7 Nov 2024 20:02:56 +0200 Subject: [PATCH 6/6] cmd: add listburns command --- cmd/tapcli/assets.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/cmd/tapcli/assets.go b/cmd/tapcli/assets.go index bd67655e2..a477457dc 100644 --- a/cmd/tapcli/assets.go +++ b/cmd/tapcli/assets.go @@ -29,6 +29,7 @@ var assetsCommands = []cli.Command{ listAssetBalancesCommand, sendAssetsCommand, burnAssetsCommand, + listBurnsCommand, listTransfersCommand, fetchMetaCommand, }, @@ -52,6 +53,7 @@ var ( assetShowUnconfMintsName = "show_unconfirmed_mints" assetGroupKeyName = "group_key" assetGroupAnchorName = "group_anchor" + anchorTxidName = "anchor_txid" batchKeyName = "batch_key" groupByGroupName = "by_group" assetIDName = "asset_id" @@ -858,6 +860,71 @@ func burnAssets(ctx *cli.Context) error { return nil } +var listBurnsCommand = cli.Command{ + Name: "listburns", + Usage: "list burnt assets", + Description: ` + List assets that have been burned by this daemon. These are assets that + have been destroyed and are no longer spendable. + + Some filters may be used to return more specific results. + `, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: assetIDName, + Usage: "the asset ID of the burnt asset", + }, + cli.StringFlag{ + Name: assetGroupKeyName, + Usage: "the group key of the burnt asset", + }, + cli.StringFlag{ + Name: anchorTxidName, + Usage: "the txid of the transaction the burn was " + + "anchored to", + }, + }, + Action: listBurns, +} + +func listBurns(ctx *cli.Context) error { + assetIDHex := ctx.String(assetIDName) + assetIDBytes, err := hex.DecodeString(assetIDHex) + if err != nil { + return fmt.Errorf("invalid asset ID: %w", err) + } + + groupKeyHex := ctx.String(assetGroupKeyName) + groupKeyBytes, err := hex.DecodeString(groupKeyHex) + if err != nil { + return fmt.Errorf("invalid group key: %w", err) + } + + anchorTxidStr := ctx.String(anchorTxidName) + anchorTxid, err := hex.DecodeString(anchorTxidStr) + if err != nil { + return fmt.Errorf("invalid anchor txid: %w", err) + } + + ctxc := getContext() + client, cleanUp := getClient(ctx) + defer cleanUp() + + resp, err := client.ListBurns( + ctxc, &taprpc.ListBurnsRequest{ + AssetId: assetIDBytes, + TweakedGroupKey: groupKeyBytes, + AnchorTxid: anchorTxid, + }, + ) + if err != nil { + return fmt.Errorf("could not list burns: %w", err) + } + + printRespJSON(resp) + return nil +} + var listTransfersCommand = cli.Command{ Name: "transfers", ShortName: "t",